Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2021-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <consensus/amount.h>6#include <key.h>7#include <script/solver.h>8#include <validation.h>9#include <wallet/coincontrol.h>10#include <wallet/spend.h>11#include <wallet/test/util.h>12#include <wallet/test/wallet_test_fixture.h>14#include <boost/test/unit_test.hpp>16namespace wallet {17BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)19BOOST_AUTO_TEST_CASE(max_signed_input_size_uses_external_outpoint)20{21const CKey key{GenerateRandomKey()};22FillableSigningProvider provider;23BOOST_REQUIRE(provider.AddKey(key));25const CTxOut txout{COIN, GetScriptForDestination(PKHash{key.GetPubKey()})};26const COutPoint outpoint{Txid{}, 0};27CCoinControl coin_control;28coin_control.Select(outpoint).SetTxOut(txout);30const int low_r{CalculateMaximumSignedInputSize(txout, COutPoint{}, &provider, /*can_grind_r=*/true, &coin_control)};31const int high_r{CalculateMaximumSignedInputSize(txout, outpoint, &provider, /*can_grind_r=*/true, &coin_control)};32BOOST_CHECK_EQUAL(high_r, low_r + 1);33}35BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)36{37CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));38auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);40// Check that a subtract-from-recipient transaction slightly less than the41// coinbase input amount does not create a change output (because it would42// be uneconomical to add and spend the output), and make sure it pays the43// leftover input amount which would have been change to the recipient44// instead of the miner.45auto check_tx = [&wallet](CAmount leftover_input_amount) {46CRecipient recipient{PubKeyDestination({}), 50 * COIN - leftover_input_amount, /*subtract_fee=*/true};47CCoinControl coin_control;48coin_control.m_feerate.emplace(10000);49coin_control.fOverrideFeeRate = true;50// We need to use a change type with high cost of change so that the leftover amount will be dropped to fee instead of added as a change output51coin_control.m_change_type = OutputType::LEGACY;52auto res = CreateTransaction(*wallet, {recipient}, /*change_pos=*/std::nullopt, coin_control);53BOOST_CHECK(res);54const auto& txr = *res;55BOOST_CHECK_EQUAL(txr.tx->vout.size(), 1);56BOOST_CHECK_EQUAL(txr.tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - txr.fee);57BOOST_CHECK_GT(txr.fee, 0);58return txr.fee;59};61// Send full input amount to recipient, check that only nonzero fee is62// subtracted (to_reduce == fee).63const CAmount fee{check_tx(0)};65// Send slightly less than full input amount to recipient, check leftover66// input amount is paid to recipient not the miner (to_reduce == fee - 123)67BOOST_CHECK_EQUAL(fee, check_tx(123));69// Send full input minus fee amount to recipient, check leftover input70// amount is paid to recipient not the miner (to_reduce == 0)71BOOST_CHECK_EQUAL(fee, check_tx(fee));73// Send full input minus more than the fee amount to recipient, check74// leftover input amount is paid to recipient not the miner (to_reduce ==75// -123). This overpays the recipient instead of overpaying the miner more76// than double the necessary fee.77BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));78}80BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup)81{82// Verify that the wallet's Coin Selection process does not include pre-selected inputs twice in a transaction.84// Add 4 spendable UTXO, 50 BTC each, to the wallet (total balance 200 BTC)85for (int i = 0; i < 4; i++) CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));86auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);88LOCK(wallet->cs_wallet);89auto available_coins = AvailableCoins(*wallet);90std::vector<COutput> coins = available_coins.All();91// Preselect the first 3 UTXO (150 BTC total)92std::set<COutPoint> preset_inputs = {coins[0].outpoint, coins[1].outpoint, coins[2].outpoint};94// Try to create a tx that spends more than what preset inputs + wallet selected inputs are covering for.95// The wallet can cover up to 200 BTC, and the tx target is 299 BTC.96std::vector<CRecipient> recipients{{*Assert(wallet->GetNewDestination(OutputType::BECH32, "dummy")),97/*nAmount=*/299 * COIN, /*fSubtractFeeFromAmount=*/true}};98CCoinControl coin_control;99coin_control.m_allow_other_inputs = true;100for (const auto& outpoint : preset_inputs) {101coin_control.Select(outpoint);102}104// Attempt to send 299 BTC from a wallet that only has 200 BTC. The wallet should exclude105// the preset inputs from the pool of available coins, realize that there is not enough106// money to fund the 299 BTC payment, and fail with "Insufficient funds".107//108// Even with SFFO, the wallet can only afford to send 200 BTC.109// If the wallet does not properly exclude preset inputs from the pool of available coins110// prior to coin selection, it may create a transaction that does not fund the full payment111// amount or, through SFFO, incorrectly reduce the recipient's amount by the difference112// between the original target and the wrongly counted inputs (in this case 99 BTC)113// so that the recipient's amount is no longer equal to the user's selected target of 299 BTC.115// First case, use 'subtract_fee_from_outputs=true'116BOOST_CHECK(!CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control));118// Second case, don't use 'subtract_fee_from_outputs'.119recipients[0].fSubtractFeeFromAmount = false;120BOOST_CHECK(!CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control));121}123BOOST_AUTO_TEST_SUITE_END()124} // namespace wallet
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.