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.
1252# Select the preset inputs1253coins = wallet.listunspent()1254preset_inputs = [coins[0], coins[1], coins[2]]1256# Now let's create the tx creation options1257options = {1258"inputs": preset_inputs,1259"add_inputs": True, # automatically add coins from the wallet to fulfill the target1260"subtract_fee_from_outputs": [0], # deduct fee from first output1261"add_to_wallet": False,1262"fee_rate": self.fee_rate_sats_per_vb1263}1265# Attempt to send 29 BTC from a wallet that only has 20 BTC. The wallet should exclude1266# the preset inputs from the pool of available coins, realize that there is not enough1267# money to fund the 29 BTC payment, and fail with "Insufficient funds".1268#1269# Even with SFFO, the wallet can only afford to send 20 BTC.1270# If the wallet does not properly exclude preset inputs from the pool of available coins1271# prior to coin selection, it may create a transaction that does not fund the full payment1272# amount or, through SFFO, incorrectly reduce the recipient's amount by the difference1273# between the original target and the wrongly counted inputs (in this case 9 BTC)1274# so that the recipient's amount is no longer equal to the user's selected target of 29 BTC.1276# First case, use 'subtract_fee_from_outputs = true'1277assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options)1279# Second case, don't use 'subtract_fee_from_outputs'1280del options["subtract_fee_from_outputs"]1281assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options)1283self.nodes[2].unloadwallet("test_preset_inputs_selection")1285def test_weight_calculation(self):1286self.log.info("Test weight calculation with external inputs")1288self.nodes[2].createwallet("test_weight_calculation")1289wallet = self.nodes[2].get_wallet_rpc("test_weight_calculation")1291addr = wallet.getnewaddress(address_type="bech32")1292ext_addr = self.nodes[0].getnewaddress(address_type="bech32")1293utxo, ext_utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 5}, {ext_addr: 5}])1295self.nodes[0].sendtoaddress(wallet.getnewaddress(address_type="bech32"), 5, fee_rate=self.fee_rate_sats_per_vb)1296self.generate(self.nodes[0], 1)1298rawtx = wallet.createrawtransaction([utxo], [{self.nodes[0].getnewaddress(address_type="bech32"): 8}])1299fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32")1300# with 71-byte signatures we should expect following tx size1301# 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)1302tx_size = ceil(10 + 41*2 + 31*2 + (2 + 107*2)/4)1303assert_equal(fundedtx['fee'] * COIN, tx_size * 10)1305# Using the other output should have 72 byte sigs1306rawtx = wallet.createrawtransaction([ext_utxo], [{self.nodes[0].getnewaddress(): 13}])1307ext_desc = self.nodes[0].getaddressinfo(ext_addr)["desc"]1308fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32", solving_data={"descriptors": [ext_desc]})1309# tx overhead (10) + 3 inputs (41 each) + 2 p2wpkh(31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 bytes sig witnesses (107 each) + p2wpkh 72 byte sig witness (108)) / witness scaling factor (4)1310tx_size = ceil(10 + 41*3 + 31*2 + (2 + 107*2 + 108)/4)1311assert_equal(fundedtx['fee'] * COIN, tx_size * 10)1313self.nodes[2].unloadwallet("test_weight_calculation")1315def test_weight_limits(self):1316self.log.info("Test weight limits")1318self.nodes[2].createwallet("test_weight_limits")1319wallet = self.nodes[2].get_wallet_rpc("test_weight_limits")1321outputs = []1322for _ in range(1472):1323outputs.append({wallet.getnewaddress(address_type="legacy"): 0.1})1324txid = self.nodes[0].send(outputs=outputs, change_position=0, fee_rate=self.fee_rate_sats_per_vb)["txid"]1325self.generate(self.nodes[0], 1)1327# 272 WU per input (273 when high-s); picking 1471 inputs will exceed the max standard tx weight.1328rawtx = wallet.createrawtransaction([], [{wallet.getnewaddress(): 0.1 * 1471}])1330# 1) Try to fund transaction only using the preset inputs (pick all 1472 inputs to cover the fee)1331input_weights = []1332for i in range(1, 1473): # skip first output as it is the parent tx change output1333input_weights.append({"txid": txid, "vout": i, "weight": 273})1334assert_raises_rpc_error(-4, "Transaction too large", wallet.fundrawtransaction, hexstring=rawtx, input_weights=input_weights)1336# 2) Let the wallet fund the transaction1337assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",1338wallet.fundrawtransaction, hexstring=rawtx)1340# 3) Pre-select some inputs and let the wallet fill-up the remaining amount1341inputs = input_weights[0:1000]1342assert_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",1343wallet.fundrawtransaction, hexstring=rawtx, input_weights=inputs)1345self.nodes[2].unloadwallet("test_weight_limits")1347def test_include_unsafe(self):1348self.log.info("Test fundrawtxn with unsafe inputs")1350self.nodes[0].createwallet("unsafe")1351wallet = self.nodes[0].get_wallet_rpc("unsafe")1353# We receive unconfirmed funds from external keys (unsafe outputs).1354addr = wallet.getnewaddress()1355inputs = []1356for i in range(0, 2):1357utxo = self.create_outpoints(self.nodes[2], outputs=[{addr: 5}])[0]1358inputs.append((utxo['txid'], utxo['vout']))1359self.sync_mempools()1361# Unsafe inputs are ignored by default.1362rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 7.5}])1363assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, rawtx)1365# But we can opt-in to use them for funding.1366fundedtx = wallet.fundrawtransaction(rawtx, include_unsafe=True, fee_rate=self.fee_rate_sats_per_vb)1367tx_dec = wallet.decoderawtransaction(fundedtx['hex'])1368assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])1369signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])1370assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]1372# And we can also use them once they're confirmed.1373self.generate(self.nodes[0], 1)1374fundedtx = wallet.fundrawtransaction(rawtx, include_unsafe=False, fee_rate=self.fee_rate_sats_per_vb)1375tx_dec = wallet.decoderawtransaction(fundedtx['hex'])1376assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])1377signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])1378assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]1379self.nodes[0].unloadwallet("unsafe")1381def test_22670(self):1382# In issue #22670, it was observed that ApproximateBestSubset may1383# choose enough value to cover the target amount but not enough to cover the transaction fees.1384# This leads to a transaction whose actual transaction feerate is lower than expected.1385# However at normal feerates, the difference between the effective value and the real value1386# that this bug is not detected because the transaction fee must be at least 0.01 BTC (the minimum change value).1387# Otherwise the targeted minimum change value will be enough to cover the transaction fees that were not1388# being accounted for. So the minimum relay fee is set to 0.1 BTC/kvB in this test.1389self.log.info("Test issue 22670 ApproximateBestSubset bug")1390# Make sure the default wallet will not be loaded when restarted with a high minrelaytxfee1391self.nodes[0].unloadwallet(self.default_wallet_name, False)1392feerate = Decimal("0.1")1393self.restart_node(0, [f"-minrelaytxfee={feerate}", "-discardfee=0"]) # Set high minrelayfee, set discardfee to 0 for easier calculation1395self.nodes[0].loadwallet(self.default_wallet_name, True)1396funds = self.nodes[0].get_wallet_rpc(self.default_wallet_name)1397self.nodes[0].createwallet(wallet_name="tester")1398tester = self.nodes[0].get_wallet_rpc("tester")1400# Because this test is specifically for ApproximateBestSubset, the target value must be greater1401# than any single input available, and require more than 1 input. So we make 3 outputs1402for i in range(0, 3):1403funds.sendtoaddress(tester.getnewaddress(address_type="bech32"), 1)1404self.generate(self.nodes[0], 1, sync_fun=self.no_op)1406# Create transactions in order to calculate fees for the target bounds that can trigger this bug1407change_tx = tester.fundrawtransaction(tester.createrawtransaction([], [{funds.getnewaddress(): 1.5}]))1408tx = tester.createrawtransaction([], [{funds.getnewaddress(): 2}])1409no_change_tx = tester.fundrawtransaction(tx, subtractFeeFromOutputs=[0])1411overhead_fees = feerate * len(tx) / 2 / 10001412cost_of_change = change_tx["fee"] - no_change_tx["fee"]1413fees = no_change_tx["fee"]1414assert_greater_than(fees, 0.01)1416def do_fund_send(target):1417create_tx = tester.createrawtransaction([], [{funds.getnewaddress(): target}])1418funded_tx = tester.fundrawtransaction(create_tx)1419signed_tx = tester.signrawtransactionwithwallet(funded_tx["hex"])1420assert signed_tx["complete"]1421decoded_tx = tester.decoderawtransaction(signed_tx["hex"])1422assert_equal(len(decoded_tx["vin"]), 3)1423assert tester.testmempoolaccept([signed_tx["hex"]])[0]["allowed"]1425# We want to choose more value than is available in 2 inputs when considering the fee,1426# but not enough to need 3 inputs when not considering the fee.1427# So the target value must be at least 2.00000001 - fee.1428lower_bound = Decimal("2.00000001") - fees1429# The target value must be at most 2 - cost_of_change - not_input_fees - min_change (these are all1430# included in the target before ApproximateBestSubset).1431upper_bound = Decimal("2.0") - cost_of_change - overhead_fees - Decimal("0.01")1432assert_greater_than_or_equal(upper_bound, lower_bound)1433do_fund_send(lower_bound)1434do_fund_send(upper_bound)1436self.restart_node(0)1437self.connect_nodes(0, 1)1438self.connect_nodes(0, 2)1439self.connect_nodes(0, 3)1441def test_feerate_rounding(self):1442self.log.info("Test that rounding of GetFee does not result in an assertion")1444self.nodes[1].createwallet("roundtest")1445w = self.nodes[1].get_wallet_rpc("roundtest")1447addr = w.getnewaddress(address_type="bech32")1448self.nodes[0].sendtoaddress(addr, 1, fee_rate=self.fee_rate_sats_per_vb)1449self.generate(self.nodes[0], 1)1451# A P2WPKH input costs 68 vbytes; With a single P2WPKH output, the rest of the tx is 42 vbytes for a total of 110 vbytes.1452# At a feerate of 1.85 sat/vb, the input will need a fee of 125.8 sats and the rest 77.7 sats1453# The entire tx fee should be 203.5 sats.1454# Coin selection rounds the fee individually instead of at the end (due to how CFeeRate::GetFee works).1455# If rounding down (which is the incorrect behavior), then the calculated fee will be 125 + 77 = 202.1456# If rounding up, then the calculated fee will be 126 + 78 = 204.1457# In the former case, the calculated needed fee is higher than the actual fee being paid, so an assertion is reached1458# To test this does not happen, we subtract 202 sats from the input value. If working correctly, this should1459# fail with insufficient funds rather than bitcoind asserting.1460rawtx = w.createrawtransaction(inputs=[], outputs=[{self.nodes[0].getnewaddress(address_type="bech32"): 1 - 0.00000202}])1461expected_err_msg = "The total exceeds your balance when the 0.00000078 transaction fee is included."1462assert_raises_rpc_error(-4, expected_err_msg, w.fundrawtransaction, rawtx, fee_rate=1.85)1464def test_input_confs_control(self):1465self.nodes[0].createwallet("minconf")1466wallet = self.nodes[0].get_wallet_rpc("minconf")1468# Fund the wallet with different chain heights1469for _ in range(2):1470self.nodes[2].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1}, fee_rate=self.fee_rate_sats_per_vb)1471self.generate(self.nodes[2], 1)1473unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5, fee_rate=self.fee_rate_sats_per_vb)1475self.log.info("Crafting TX using an unconfirmed input")1476target_address = self.nodes[2].getnewaddress()1477raw_tx1 = wallet.createrawtransaction([], {target_address: 0.1}, 0, True)1478funded_tx1 = wallet.fundrawtransaction(raw_tx1, {'fee_rate': 1, 'maxconf': 0})['hex']1480# Make sure we only had the one input1481tx1_inputs = self.nodes[0].decoderawtransaction(funded_tx1)['vin']1482assert_equal(len(tx1_inputs), 1)1484utxo1 = tx1_inputs[0]1485assert_equal(unconfirmed_txid, utxo1['txid'])1487final_tx1 = wallet.signrawtransactionwithwallet(funded_tx1)['hex']1488txid1 = self.nodes[0].sendrawtransaction(final_tx1)1490mempool = self.nodes[0].getrawmempool()1491assert txid1 in mempool1493self.log.info("Fail to craft a new TX with minconf above highest one")1494# Create a replacement tx to 'final_tx1' that has 1 BTC target instead of 0.1.1495raw_tx2 = wallet.createrawtransaction([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1})1496assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx2, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10})1498self.log.info("Fail to broadcast a new TX with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs")1499# Now fund 'raw_tx2' to fulfill the total target (1 BTC) by using all the wallet unconfirmed outputs.1500# As it was created with the first unconfirmed output, 'raw_tx2' only has 0.1 BTC covered (need to fund 0.9 BTC more).
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.