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.
752self.nodes[3].loadwallet('wwatch')753wwatch = self.nodes[3].get_wallet_rpc('wwatch')754# Setup change addresses for the watchonly wallet755desc_import = [{756"desc": descsum_create("wpkh(tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H/1/*)"),757"timestamp": "now",758"internal": True,759"active": True,760"keypool": True,761"range": [0, 100],762"watchonly": True,763}]764wwatch.importdescriptors(desc_import)766result = wwatch.fundrawtransaction(rawtx)767res_dec = self.nodes[0].decoderawtransaction(result["hex"])768assert_equal(len(res_dec["vin"]), 1)769assert_equal(res_dec["vin"][0]["txid"], self.watchonly_utxo['txid'])771assert "fee" in result.keys()772assert_greater_than(result["changepos"], -1)774wwatch.unloadwallet()776def test_all_watched_funds(self):777self.log.info("Test fundrawtxn using entirety of watched funds")779inputs = []780outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount}781rawtx = self.nodes[3].createrawtransaction(inputs, outputs)783self.nodes[3].loadwallet('wwatch')784wwatch = self.nodes[3].get_wallet_rpc('wwatch')785w3 = self.nodes[3].get_wallet_rpc(self.default_wallet_name)786result = wwatch.fundrawtransaction(rawtx, changeAddress=w3.getrawchangeaddress(), subtractFeeFromOutputs=[0])787res_dec = self.nodes[0].decoderawtransaction(result["hex"])788assert_equal(len(res_dec["vin"]), 1)789assert_equal(res_dec["vin"][0]["txid"], self.watchonly_utxo['txid'])791assert_greater_than(result["fee"], 0)792assert_equal(result["changepos"], -1)793assert_equal(result["fee"] + res_dec["vout"][0]["value"], self.watchonly_amount)795signedtx = wwatch.signrawtransactionwithwallet(result["hex"])796assert not signedtx["complete"]797signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])798assert signedtx["complete"]799self.nodes[0].sendrawtransaction(signedtx["hex"])800self.generate(self.nodes[0], 1)802wwatch.unloadwallet()804def test_option_feerate(self):805self.log.info("Test fundrawtxn with explicit fee rates (fee_rate sat/vB and feeRate BTC/kvB)")806node = self.nodes[3]807# Make sure there is exactly one input so coin selection can't skew the result.808assert_equal(len(self.nodes[3].listunspent(1)), 1)809inputs = []810outputs = {node.getnewaddress() : 1}811rawtx = node.createrawtransaction(inputs, outputs)813result = node.fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb) # uses self.min_relay_tx_fee (set by fee_rate in sat/vB)814btc_kvb_to_sat_vb = 100000 # (1e5)815result1 = node.fundrawtransaction(rawtx, fee_rate=str(2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee))816result2 = node.fundrawtransaction(rawtx, feeRate=2 * self.min_relay_tx_fee)817result3 = node.fundrawtransaction(rawtx, fee_rate=10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee)818result4 = node.fundrawtransaction(rawtx, feeRate=str(10 * self.min_relay_tx_fee))820result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])821assert_fee_amount(result1['fee'], count_bytes(result1['hex']), 2 * result_fee_rate)822assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)823assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)824assert_fee_amount(result4['fee'], count_bytes(result4['hex']), 10 * result_fee_rate)826# Test that funding non-standard "zero-fee" transactions is valid.827for param, zero_value in product(["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]):828assert_equal(self.nodes[3].fundrawtransaction(rawtx, {param: zero_value})["fee"], 0)830# With no arguments passed, expect fee of 141 satoshis.831assert_approx(node.fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)["fee"], vexp=0.00000141, vspan=0.00000001)832# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.833result = node.fundrawtransaction(rawtx, fee_rate=10000)834assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)836self.log.info("Test fundrawtxn with invalid estimate_mode settings")837for k, v in {"number": 42, "object": {"foo": "bar"}}.items():838assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string",839node.fundrawtransaction, rawtx, estimate_mode=v, conf_target=0.1, add_inputs=True)840for mode in ["", "foo", Decimal("3.141592")]:841assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"',842node.fundrawtransaction, rawtx, estimate_mode=mode, conf_target=0.1, add_inputs=True)844self.log.info("Test fundrawtxn with invalid conf_target settings")845for mode in ["unset", "economical", "conservative"]:846self.log.debug("{}".format(mode))847for k, v in {"string": "", "object": {"foo": "bar"}}.items():848assert_raises_rpc_error(-3, f"JSON value of type {k} for field conf_target is not of expected type number",849node.fundrawtransaction, rawtx, estimate_mode=mode, conf_target=v, add_inputs=True)850for n in [-1, 0, 1009]:851assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees/block_policy_estimator.h852node.fundrawtransaction, rawtx, estimate_mode=mode, conf_target=n, add_inputs=True)854self.log.info("Test invalid fee rate settings")855for param, value in {("fee_rate", 100000), ("feeRate", 1.000)}:856assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",857node.fundrawtransaction, rawtx, add_inputs=True, **{param: value})858assert_raises_rpc_error(-3, "Amount out of range",859node.fundrawtransaction, rawtx, add_inputs=True, **{param: -1})860assert_raises_rpc_error(-3, "Amount is not a number or string",861node.fundrawtransaction, rawtx, add_inputs=True, **{param: {"foo": "bar"}})862# Test fee rate values that don't pass fixed-point parsing checks.863for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:864assert_raises_rpc_error(-3, "Invalid amount", node.fundrawtransaction, rawtx, add_inputs=True, **{param: invalid_value})865# Test fee_rate values that cannot be represented in sat/vB.866for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:867assert_raises_rpc_error(-3, "Invalid amount",868node.fundrawtransaction, rawtx, fee_rate=invalid_value, add_inputs=True)870self.log.info("Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed")871node.fundrawtransaction(rawtx, fee_rate=0.999, add_inputs=True)872node.fundrawtransaction(rawtx, feeRate=0.00000999, add_inputs=True)874self.log.info("- raises RPC error if both feeRate and fee_rate are passed")875assert_raises_rpc_error(-8, "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)",876node.fundrawtransaction, rawtx, fee_rate=0.1, feeRate=0.1, add_inputs=True)878self.log.info("- raises RPC error if both feeRate and estimate_mode passed")879assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and feeRate",880node.fundrawtransaction, rawtx, estimate_mode="economical", feeRate=0.1, add_inputs=True)882for param in ["feeRate", "fee_rate"]:883self.log.info("- raises RPC error if both {} and conf_target are passed".format(param))884assert_raises_rpc_error(-8, "Cannot specify both conf_target and {}. Please provide either a confirmation "885"target in blocks for automatic fee estimation, or an explicit fee rate.".format(param),886node.fundrawtransaction, rawtx, {param: 1, "conf_target": 1, "add_inputs": True})888self.log.info("- raises RPC error if both fee_rate and estimate_mode are passed")889assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and fee_rate",890node.fundrawtransaction, rawtx, fee_rate=1, estimate_mode="economical", add_inputs=True)892def test_address_reuse(self):893"""Test no address reuse occurs."""894self.log.info("Test fundrawtxn does not reuse addresses")896rawtx = self.nodes[3].createrawtransaction(inputs=[], outputs={self.nodes[3].getnewaddress(): 1})897result3 = self.nodes[3].fundrawtransaction(rawtx)898res_dec = self.nodes[0].decoderawtransaction(result3["hex"])899changeaddress = ""900for out in res_dec['vout']:901if out['value'] > 1.0:902changeaddress += out['scriptPubKey']['address']903assert_not_equal(changeaddress, "")904nextaddr = self.nodes[3].getnewaddress()905# Now the change address key should be removed from the keypool.906assert_not_equal(changeaddress, nextaddr)908def test_option_subtract_fee_from_outputs(self):909self.log.info("Test fundrawtxn subtractFeeFromOutputs option")911# Make sure there is exactly one input so coin selection can't skew the result.912assert_equal(len(self.nodes[3].listunspent(1)), 1)914inputs = []915outputs = {self.nodes[2].getnewaddress(): 1}916rawtx = self.nodes[3].createrawtransaction(inputs, outputs)918# Test subtract fee from outputs with feeRate (BTC/kvB)919result = [self.nodes[3].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb),920self.nodes[3].fundrawtransaction(rawtx, subtractFeeFromOutputs=[], fee_rate=self.fee_rate_sats_per_vb), # empty subtraction list921self.nodes[3].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0], fee_rate=self.fee_rate_sats_per_vb), # uses self.min_relay_tx_fee (set by fee_rate in sat/vB)922self.nodes[3].fundrawtransaction(rawtx, feeRate=2 * self.min_relay_tx_fee),923self.nodes[3].fundrawtransaction(rawtx, feeRate=2 * self.min_relay_tx_fee, subtractFeeFromOutputs=[0]),]924dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]925output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]926change = [d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)]928assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee'])929assert_equal(result[3]['fee'], result[4]['fee'])930assert_equal(change[0], change[1])931assert_equal(output[0], output[1])932assert_equal(output[0], output[2] + result[2]['fee'])933assert_equal(change[0] + result[0]['fee'], change[2])934assert_equal(output[3], output[4] + result[4]['fee'])935assert_equal(change[3] + result[3]['fee'], change[4])937# Test subtract fee from outputs with fee_rate (sat/vB)938btc_kvb_to_sat_vb = 100000 # (1e5)939result = [self.nodes[3].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb), # uses self.min_relay_tx_fee (set by fee_rate in sat/vB)940self.nodes[3].fundrawtransaction(rawtx, subtractFeeFromOutputs=[], fee_rate=self.fee_rate_sats_per_vb), # empty subtraction list941self.nodes[3].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0], fee_rate=self.fee_rate_sats_per_vb), # uses self.min_relay_tx_fee (set by fee_rate in sat/vB)942self.nodes[3].fundrawtransaction(rawtx, fee_rate=2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee),943self.nodes[3].fundrawtransaction(rawtx, fee_rate=2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee, subtractFeeFromOutputs=[0]),]944dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]945output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]946change = [d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)]948assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee'])949assert_equal(result[3]['fee'], result[4]['fee'])950assert_equal(change[0], change[1])951assert_equal(output[0], output[1])952assert_equal(output[0], output[2] + result[2]['fee'])953assert_equal(change[0] + result[0]['fee'], change[2])954assert_equal(output[3], output[4] + result[4]['fee'])955assert_equal(change[3] + result[3]['fee'], change[4])957inputs = []958outputs = {self.nodes[2].getnewaddress(): value for value in (1.0, 1.1, 1.2, 1.3)}959rawtx = self.nodes[3].createrawtransaction(inputs, outputs)961result = [self.nodes[3].fundrawtransaction(rawtx),962# Split the fee between outputs 0, 2, and 3, but not output 1.963self.nodes[3].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0, 2, 3])]965dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']),966self.nodes[3].decoderawtransaction(result[1]['hex'])]968# Nested list of non-change output amounts for each transaction.969output = [[out['value'] for i, out in enumerate(d['vout']) if i != r['changepos']]970for d, r in zip(dec_tx, result)]972# List of differences in output amounts between normal and subtractFee transactions.973share = [o0 - o1 for o0, o1 in zip(output[0], output[1])]975# Output 1 is the same in both transactions.976assert_equal(share[1], 0)978# The other 3 outputs are smaller as a result of subtractFeeFromOutputs.979assert_greater_than(share[0], 0)980assert_greater_than(share[2], 0)981assert_greater_than(share[3], 0)983# Outputs 2 and 3 take the same share of the fee.984assert_equal(share[2], share[3])986# Output 0 takes at least as much share of the fee, and no more than 2987# satoshis more, than outputs 2 and 3.988assert_greater_than_or_equal(share[0], share[2])989assert_greater_than_or_equal(share[2] + Decimal(2e-8), share[0])991# The fee is the same in both transactions.992assert_equal(result[0]['fee'], result[1]['fee'])994# The total subtracted from the outputs is equal to the fee.995assert_equal(share[0] + share[2] + share[3], result[0]['fee'])997def test_subtract_fee_with_presets(self):998self.log.info("Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient")1000addr = self.nodes[0].getnewaddress()
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.