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.
251else:252utxo_filter = self._utxos253if confirmed_only:254utxo_filter = filter(lambda utxo: utxo['confirmations'] > 0, utxo_filter)255utxos = deepcopy(list(utxo_filter))256if mark_as_spent:257self._utxos = []258return utxos260def send_self_transfer(self, *, from_node, **kwargs):261"""Call create_self_transfer and send the transaction."""262tx = self.create_self_transfer(**kwargs)263self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])264return tx266def send_to(self, *, from_node, scriptPubKey, amount, fee=1000):267"""268Create and send a tx with an output to a given scriptPubKey/amount,269plus a change output to our internal address. To keep things simple, a270fixed fee given in Satoshi is used.272Note that this method fails if there is no single internal utxo273available that can cover the cost for the amount and the fixed fee274(the utxo with the largest value is taken).275"""276tx = self.create_self_transfer(fee_rate=0)["tx"]277assert_greater_than_or_equal(tx.vout[0].nValue, amount + fee)278tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet279tx.vout.append(CTxOut(amount, scriptPubKey)) # arbitrary output -> to be returned280txid = self.sendrawtransaction(from_node=from_node, tx_hex=tx.serialize().hex())281return {282"sent_vout": 1,283"txid": txid,284"wtxid": tx.wtxid_hex,285"hex": tx.serialize().hex(),286"tx": tx,287}289def send_self_transfer_multi(self, *, from_node, **kwargs):290"""Call create_self_transfer_multi and send the transaction."""291tx = self.create_self_transfer_multi(**kwargs)292self.sendrawtransaction(from_node=from_node, tx_hex=tx["hex"])293return tx295def create_self_transfer_multi(296self,297*,298utxos_to_spend: Optional[list[dict]] = None,299num_outputs=1,300amount_per_output=0,301version=2,302locktime=0,303sequence=0,304fee_per_output=1000,305target_vsize=0,306confirmed_only=False,307):308"""309Create and return a transaction that spends the given UTXOs and creates a310certain number of outputs with equal amounts. The output amounts can be311set by amount_per_output or automatically calculated with a fee_per_output.312"""313utxos_to_spend = utxos_to_spend or [self.get_utxo(confirmed_only=confirmed_only)]314sequence = [sequence] * len(utxos_to_spend) if type(sequence) is int else sequence315assert_equal(len(utxos_to_spend), len(sequence))317# calculate output amount318inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend])319outputs_value_total = inputs_value_total - fee_per_output * num_outputs320amount_per_output = amount_per_output or (outputs_value_total // num_outputs)321assert amount_per_output > 0322outputs_value_total = amount_per_output * num_outputs323fee = Decimal(inputs_value_total - outputs_value_total) / COIN325# create tx326tx = CTransaction()327tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=seq) for utxo_to_spend, seq in zip(utxos_to_spend, sequence)]328tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)]329tx.version = version330tx.nLockTime = locktime332self.sign_tx(tx)334if target_vsize:335self._bulk_tx(tx, target_vsize)337txid = tx.txid_hex338return {339"new_utxos": [self._create_utxo(340txid=txid,341vout=i,342value=Decimal(tx.vout[i].nValue) / COIN,343height=0,344coinbase=False,345confirmations=0,346) for i in range(len(tx.vout))],347"fee": fee,348"txid": txid,349"wtxid": tx.wtxid_hex,350"hex": tx.serialize().hex(),351"tx": tx,352}354def create_self_transfer(355self,356*,357fee_rate=Decimal("0.003"),358fee=Decimal("0"),359utxo_to_spend=None,360target_vsize=0,361confirmed_only=False,362**kwargs,363):364"""Create and return a tx with the specified fee. If fee is 0, use fee_rate, where the resulting fee may be exact or at most one satoshi higher than needed."""365utxo_to_spend = utxo_to_spend or self.get_utxo(confirmed_only=confirmed_only)366assert fee_rate >= 0367assert fee >= 0368# calculate fee369if self._mode in (MiniWalletMode.RAW_OP_TRUE, MiniWalletMode.ADDRESS_OP_TRUE):370vsize = Decimal(104) # anyone-can-spend371elif self._mode == MiniWalletMode.RAW_P2PK:372vsize = Decimal(168) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)373else:374assert False375if target_vsize and not fee: # respect fee_rate if target vsize is passed376fee = get_fee(target_vsize, fee_rate)377send_value = utxo_to_spend["value"] - (fee or (fee_rate * vsize / 1000))378if send_value <= 0:379raise RuntimeError(f"UTXO value {utxo_to_spend['value']} is too small to cover fees {(fee or (fee_rate * vsize / 1000))}")380# create tx381tx = self.create_self_transfer_multi(382utxos_to_spend=[utxo_to_spend],383amount_per_output=int(COIN * send_value),384target_vsize=target_vsize,385**kwargs,386)387if not target_vsize:388assert_equal(tx["tx"].get_vsize(), vsize)389tx["new_utxo"] = tx.pop("new_utxos")[0]391return tx393def sendrawtransaction(self, *, from_node, tx_hex, maxfeerate=0, **kwargs):394txid = from_node.sendrawtransaction(hexstring=tx_hex, maxfeerate=maxfeerate, **kwargs)395self.scan_tx(from_node.decoderawtransaction(tx_hex))396return txid398def create_self_transfer_chain(self, *, chain_length, utxo_to_spend=None):399"""400Create a "chain" of chain_length transactions. The nth transaction in401the chain is a child of the n-1th transaction and parent of the n+1th transaction.402"""403chaintip_utxo = utxo_to_spend or self.get_utxo()404chain = []406for _ in range(chain_length):407tx = self.create_self_transfer(utxo_to_spend=chaintip_utxo)408chaintip_utxo = tx["new_utxo"]409chain.append(tx)411return chain413def send_self_transfer_chain(self, *, from_node, **kwargs):414"""Create and send a "chain" of chain_length transactions. The nth transaction in415the chain is a child of the n-1th transaction and parent of the n+1th transaction.417Returns a list of objects for each tx (see create_self_transfer_multi).418"""419chain = self.create_self_transfer_chain(**kwargs)420for t in chain:421self.sendrawtransaction(from_node=from_node, tx_hex=t["hex"])422return chain425class NodeSigner:426"""Simple wallet replacement that delegates signing of existing raw transactions to a node by427using the `signrawtransactionwithkey` RPC. This can be used for spending from widespread428output types (P2PKH, P2WPKH, P2SH-P2WPKH, P2TR) without having the wallet compiled in."""429def __init__(self, node):430self._node = node431self._key_entries = []433def getnewaddress(self, address_type='legacy'):434(seckey, pubkey), spk, address = getnewdestination(address_type)435redeem_script = key_to_p2wpkh_script(pubkey) if address_type == 'p2sh-segwit' else None436self._key_entries.append({"seckey_wif": bytes_to_wif(seckey.get_bytes()), "output_script": spk, "redeem_script": redeem_script})437return pubkey, spk, address439def listunspent(self):440needles = [descsum_create(f'raw({key_entry["output_script"].hex()})') for key_entry in self._key_entries]441scan_res = self._node.scantxoutset(action="start", scanobjects=needles)442spend_height = scan_res['height'] + 1 # coins would be spent in the next block443unspents = []444for u in scan_res['unspents']:445if u["coinbase"] and (spend_height - u["height"]) < COINBASE_MATURITY: # skip immature coins446continue447unspent = { "txid": u["txid"], "vout": u["vout"], "scriptPubKey": u["scriptPubKey"], "amount": u["amount"] }448key_entry = [ke for ke in self._key_entries if ke["output_script"] == bytes.fromhex(u["scriptPubKey"])][0]449if key_entry["redeem_script"] is not None:450unspent["redeemScript"] = key_entry["redeem_script"].hex()451unspents.append(unspent)452return unspents454def signrawtransaction(self, tx_hex, inputs):455output_scripts_to_sign = {i["scriptPubKey"] for i in inputs}456seckeys_wif = [ke["seckey_wif"] for ke in self._key_entries if ke["output_script"].hex() in output_scripts_to_sign]457return self._node.signrawtransactionwithkey(tx_hex, seckeys_wif, inputs)460def getnewdestination(address_type='bech32m'):461"""Generate a random destination of the specified type and return the462corresponding key pair, scriptPubKey and address. Supported types are463'legacy', 'p2sh-segwit', 'bech32' and 'bech32m'. Can be used when a random464destination is needed, but no compiled wallet is available (e.g. as465replacement to the getnewaddress/getaddressinfo RPCs)."""466key, pubkey = generate_keypair()467if address_type == 'legacy':468scriptpubkey = key_to_p2pkh_script(pubkey)469address = key_to_p2pkh(pubkey)470elif address_type == 'p2sh-segwit':471scriptpubkey = key_to_p2sh_p2wpkh_script(pubkey)472address = key_to_p2sh_p2wpkh(pubkey)473elif address_type == 'bech32':474scriptpubkey = key_to_p2wpkh_script(pubkey)475address = key_to_p2wpkh(pubkey)476elif address_type == 'bech32m':477tap = taproot_construct(compute_xonly_pubkey(key.get_bytes())[0])478pubkey = tap.output_pubkey479scriptpubkey = tap.scriptPubKey480address = output_key_to_p2tr(pubkey)481else:482assert False483return (key, pubkey), scriptpubkey, address
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.