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.
501outputs = {mSigObj:1.1}502rawtx = self.nodes[0].createrawtransaction(inputs, outputs)503fundedTx = self.nodes[0].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)505# Create same transaction over sendtoaddress.506txId = self.nodes[0].sendtoaddress(mSigObj, 1.1, fee_rate=self.fee_rate_sats_per_vb)507signedFee = self.nodes[0].getmempoolentry(txId)['fees']['base']509# Compare fee.510feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)511assert feeDelta >= 0 and feeDelta <= self.fee_tolerance513self.unlock_utxos(self.nodes[0])515def test_fee_4of5(self):516"""Compare fee of a standard pubkeyhash transaction."""517self.log.info("Test fundrawtxn fee with 4-of-5 addresses")518self.lock_outputs_type(self.nodes[0], "p2pkh")520# Create 4-of-5 addr.521addr1 = self.nodes[1].getnewaddress()522addr2 = self.nodes[1].getnewaddress()523addr3 = self.nodes[1].getnewaddress()524addr4 = self.nodes[1].getnewaddress()525addr5 = self.nodes[1].getnewaddress()527addr1Obj = self.nodes[1].getaddressinfo(addr1)528addr2Obj = self.nodes[1].getaddressinfo(addr2)529addr3Obj = self.nodes[1].getaddressinfo(addr3)530addr4Obj = self.nodes[1].getaddressinfo(addr4)531addr5Obj = self.nodes[1].getaddressinfo(addr5)533mSigObj = self.nodes[1].createmultisig(5344,535[536addr1Obj['pubkey'],537addr2Obj['pubkey'],538addr3Obj['pubkey'],539addr4Obj['pubkey'],540addr5Obj['pubkey'],541]542)['address']544inputs = []545outputs = {mSigObj:1.1}546rawtx = self.nodes[0].createrawtransaction(inputs, outputs)547fundedTx = self.nodes[0].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)549# Create same transaction over sendtoaddress.550txId = self.nodes[0].sendtoaddress(mSigObj, 1.1, fee_rate=self.fee_rate_sats_per_vb)551signedFee = self.nodes[0].getmempoolentry(txId)['fees']['base']553# Compare fee.554feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)555assert feeDelta >= 0 and feeDelta <= self.fee_tolerance557self.unlock_utxos(self.nodes[0])559def test_spend_2of2(self):560"""Spend a 2-of-2 multisig transaction over fundraw."""561self.log.info("Test fundpsbt spending 2-of-2 multisig")563# Create 2-of-2 addr.564addr1 = self.nodes[2].getnewaddress()565addr2 = self.nodes[2].getnewaddress()567addr1Obj = self.nodes[2].getaddressinfo(addr1)568addr2Obj = self.nodes[2].getaddressinfo(addr2)570self.nodes[2].createwallet(wallet_name='wmulti', disable_private_keys=True)571wmulti = self.nodes[2].get_wallet_rpc('wmulti')572w2 = self.nodes[2].get_wallet_rpc(self.default_wallet_name)573mSigObj = self.nodes[2].createmultisig(5742,575[576addr1Obj['pubkey'],577addr2Obj['pubkey'],578]579)580import_res = wmulti.importdescriptors([{"desc": mSigObj["descriptor"], "timestamp": "now"}])581assert_equal(import_res[0]["success"], True)583# Send 1.2 BTC to msig addr.584self.nodes[0].sendtoaddress(mSigObj["address"], 1.2, fee_rate=self.fee_rate_sats_per_vb)585self.generate(self.nodes[0], 1)587oldBalance = self.nodes[1].getbalance()588inputs = []589outputs = {self.nodes[1].getnewaddress():1.1}590funded_psbt = wmulti.walletcreatefundedpsbt(inputs=inputs, outputs=outputs, changeAddress=w2.getrawchangeaddress())['psbt']592signed_psbt = w2.walletprocesspsbt(funded_psbt)593self.nodes[2].sendrawtransaction(signed_psbt['hex'])594self.generate(self.nodes[2], 1)596# Make sure funds are received at node1.597assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())599wmulti.unloadwallet()601def test_locked_wallet(self):602self.log.info("Test fundrawtxn with locked wallet and hardened derivation")604df_wallet = self.nodes[1].get_wallet_rpc(self.default_wallet_name)605self.nodes[1].createwallet(wallet_name="locked_wallet")606wallet = self.nodes[1].get_wallet_rpc("locked_wallet")608# Add some balance to the wallet (this will be reverted at the end of the test)609df_wallet.sendall(recipients=[wallet.getnewaddress()])610self.generate(self.nodes[1], 1)612# Encrypt wallet and import descriptors613wallet.encryptwallet("test")615with WalletUnlock(wallet, "test"):616wallet.importdescriptors([{617'desc': descsum_create(f'wpkh({ExtendedPrivateKey.generate().to_string()}/0h/*h)'),618'timestamp': 'now',619'active': True620},621{622'desc': descsum_create(f'wpkh({ExtendedPrivateKey.generate().to_string()}/1h/*h)'),623'timestamp': 'now',624'active': True,625'internal': True626}])628# Drain the keypool.629wallet.getnewaddress()630wallet.getrawchangeaddress()632# Choose input633inputs = wallet.listunspent()635# Deduce exact fee to produce a changeless transaction636tx_size = 110 # Total tx size: 110 vbytes, p2wpkh -> p2wpkh. Input 68 vbytes + rest of tx is 42 vbytes.637value = inputs[0]["amount"] - get_fee(tx_size, self.min_relay_tx_fee)639outputs = {self.nodes[0].getnewaddress():value}640rawtx = wallet.createrawtransaction(inputs, outputs)641# fund a transaction that does not require a new key for the change output642funded_tx = wallet.fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)643assert_equal(funded_tx["changepos"], -1)645# fund a transaction that requires a new key for the change output646# creating the key must be impossible because the wallet is locked647outputs = {self.nodes[0].getnewaddress():value - Decimal("0.1")}648rawtx = wallet.createrawtransaction(inputs, outputs)649assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", wallet.fundrawtransaction, rawtx)651# Refill the keypool.652with WalletUnlock(wallet, "test"):653wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address655assert_raises_rpc_error(-13, "walletpassphrase", wallet.sendtoaddress, self.nodes[0].getnewaddress(), 1.2)657oldBalance = self.nodes[0].getbalance()659inputs = []660outputs = {self.nodes[0].getnewaddress():1.1}661rawtx = wallet.createrawtransaction(inputs, outputs)663fundedTx = wallet.fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)664assert_not_equal(fundedTx["changepos"], -1)666# Now we need to unlock.667with WalletUnlock(wallet, "test"):668signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex'])669wallet.sendrawtransaction(signedTx['hex'])670self.generate(self.nodes[1], 1)672# Make sure funds are received at node1.673assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())675# Restore pre-test wallet state676wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()], fee_rate=self.fee_rate_sats_per_vb)677wallet.unloadwallet()678self.generate(self.nodes[1], 1)680def test_many_inputs_fee(self):681"""Multiple (~19) inputs tx test | Compare fee."""682self.log.info("Test fundrawtxn fee with many inputs")684# Empty node1, send some small coins from node0 to node1.685self.nodes[1].sendall(recipients=[self.nodes[0].getnewaddress()], fee_rate=self.fee_rate_sats_per_vb)686self.generate(self.nodes[1], 1)688for _ in range(20):689self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01, fee_rate=self.fee_rate_sats_per_vb)690self.generate(self.nodes[0], 1)692# Fund a tx with ~20 small inputs.693inputs = []694outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}695rawtx = self.nodes[1].createrawtransaction(inputs, outputs)696fundedTx = self.nodes[1].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)698# Create same transaction over sendtoaddress.699txId = self.nodes[1].sendmany("", outputs, fee_rate=self.fee_rate_sats_per_vb)700signedFee = self.nodes[1].getmempoolentry(txId)['fees']['base']702# Compare fee.703feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)704assert feeDelta >= 0 and feeDelta <= self.fee_tolerance * 19 #~19 inputs706def test_many_inputs_send(self):707"""Multiple (~19) inputs tx test | sign/send."""708self.log.info("Test fundrawtxn sign+send with many inputs")710# Again, empty node1, send some small coins from node0 to node1.711self.nodes[1].sendall(recipients=[self.nodes[0].getnewaddress()], fee_rate=self.fee_rate_sats_per_vb)712self.generate(self.nodes[1], 1)714for _ in range(20):715self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01, fee_rate=self.fee_rate_sats_per_vb)716self.generate(self.nodes[0], 1)718# Fund a tx with ~20 small inputs.719oldBalance = self.nodes[0].getbalance()721inputs = []722outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}723rawtx = self.nodes[1].createrawtransaction(inputs, outputs)724fundedTx = self.nodes[1].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)725fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])726self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])727self.generate(self.nodes[1], 1)728assert_equal(oldBalance+Decimal('50.19000000'), self.nodes[0].getbalance()) #0.19+block reward730def test_op_return(self):731self.log.info("Test fundrawtxn with OP_RETURN and no vin")733rawtx = "0100000000010000000000000000066a047465737400000000"734dec_tx = self.nodes[2].decoderawtransaction(rawtx)736assert_equal(len(dec_tx['vin']), 0)737assert_equal(len(dec_tx['vout']), 1)739rawtxfund = self.nodes[2].fundrawtransaction(rawtx, fee_rate=self.fee_rate_sats_per_vb)740dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])742assert_greater_than(len(dec_tx['vin']), 0) # at least one vin743assert_equal(len(dec_tx['vout']), 2) # one change output added745def test_watchonly(self):746self.log.info("Test fundrawtxn using only watchonly")748inputs = []749outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount / 2}750rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
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.