Merge bitcoin/bitcoin#34382: test: wallet: Check fallbackfee default argument behavior.

3dcdb2b9ba test: wallet: Warning for excessive fallback fee. (David Gumberg)
6664e41e56 test: wallet: -fallbackfee default is 0 (David Gumberg)
d28c989243 test: wallet: refactor: fallbackfee extract common send failure checks. (David Gumberg)

Pull request description:

  In an unmerged branch of #32636 (097e00f907) I unintentionally broke default `-fallbackfee` behavior, but this was not caught by any tests. See https://github.com/bitcoin/bitcoin/pull/32636#discussion_r2706102550.

  Something like the following diff does not cause any test failures on master despite causing a behavior change:

  ```diff
  diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
  index bc27018cd2..079610fba0 100644
  --- a/src/wallet/wallet.cpp
  +++ b/src/wallet/wallet.cpp
  @@ -3048,24 +3048,24 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
       if (const auto arg{args.GetArg("-fallbackfee")}) {
           std::optional<CAmount> fallback_fee = ParseMoney(*arg);
           if (!fallback_fee) {
               error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-fallbackfee", *arg);
               return nullptr;
           } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) {
               warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") +
                                  _("This is the transaction fee you may pay when fee estimates are not available."));
           }
           walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()};
  +        // Disable fallback fee in case value was set to 0, enable if non-null value
  +        walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0;
       }

  -    // Disable fallback fee in case value was set to 0, enable if non-null value
  -    walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0;
  ```

  This PR adds a functional test check that when no `-fallbackfee` argument is set and fee estimation is not possible, that sending fails because `-fallbackfee` is disabled by default.

ACKs for top commit:
  maflcko:
    review ACK 3dcdb2b9ba 🐞
  w0xlt:
    reACK 3dcdb2b9ba
  ismaelsadeeq:
    reACK 3dcdb2b9ba 👾

Tree-SHA512: 715625673a781ba3ddfed25f0836b01c2197480bd56732fd1ce548e8969573c2a36601de0b8913b3b79a47b8149022aabcf409b62297e7c2c47b68a8843e6570
This commit is contained in:
merge-script
2026-03-30 19:50:19 +08:00

View File

@@ -2,13 +2,18 @@
# Copyright (c) 2017-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet replace-by-fee capabilities in conjunction with the fallbackfee."""
"""Test wallet fallbackfee."""
from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_raises_rpc_error
from test_framework.util import assert_equal, assert_raises_rpc_error
class WalletRBFTest(BitcoinTestFramework):
HIGH_TX_FEE_PER_KB = Decimal('0.01')
class WalletFallbackFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
@@ -16,17 +21,55 @@ class WalletRBFTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def sending_succeeds(self, node):
# Check that fallback fee is being used as a test-of-the-test.
assert_equal(
node.sendtoaddress(node.getnewaddress(), 1, verbose=True)['fee_reason'],
"Fallback fee"
)
node.fundrawtransaction(node.createrawtransaction([], {node.getnewaddress(): 1}))
assert_equal(
node.sendmany("", {node.getnewaddress(): 1}, verbose=True)["fee_reason"],
"Fallback fee"
)
def sending_fails(self, node):
assert_raises_rpc_error(-6, "Fee estimation failed", lambda: node.sendtoaddress(node.getnewaddress(), 1))
assert_raises_rpc_error(-4, "Fee estimation failed", lambda: node.fundrawtransaction(node.createrawtransaction([], {node.getnewaddress(): 1})))
assert_raises_rpc_error(-6, "Fee estimation failed", lambda: node.sendmany("", {node.getnewaddress(): 1}))
def run_test(self):
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
node = self.nodes[0]
self.generate(node, COINBASE_MATURITY + 1)
# sending a transaction without fee estimations must be possible by default on regtest
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
# By default, the test framework sets a fallback fee for nodes,
# in order to test default behavior, comment this line out.
node.replace_in_config([("fallbackfee=", "#fallbackfee=")])
self.restart_node(0)
# test sending a tx with disabled fallback fee (must fail)
# Sending a transaction with no -fallbackfee setting fails, since the
# default value is 0.
self.sending_fails(node)
# Sending a tx with explicitly disabled fallback fee fails.
self.restart_node(0, extra_args=["-fallbackfee=0"])
assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1))
assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].fundrawtransaction(self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1})))
assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendmany("", {self.nodes[0].getnewaddress(): 1}))
self.sending_fails(node)
# Sending a transaction with a fallback fee set succeeds. Use the
# largest fallbackfee value that doesn't trigger a warning.
self.restart_node(0, extra_args=[f"-fallbackfee={HIGH_TX_FEE_PER_KB}"])
self.sending_succeeds(node)
self.stop_node(0, expected_stderr='')
# Starting a node with a large fallback fee set...
excessive_fallback = HIGH_TX_FEE_PER_KB + Decimal('0.00000001')
self.start_node(0, extra_args=[f"-fallbackfee={excessive_fallback}"])
# ...works...
self.sending_succeeds(node)
# ...but results in a warning message.
expected_error = "Warning: -fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available."
self.stop_node(0, expected_stderr=expected_error)
if __name__ == '__main__':
WalletRBFTest(__file__).main()
WalletFallbackFeeTest(__file__).main()