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.
251self.nodes[1].createrawtransaction(utxo_dicts, recipient_dict))['hex'])253# send from nodes[2] using utxo to nodes[3]254recipient_dict2 = {255self.nodes[3].getnewaddress(): 1,256self.nodes[2].getnewaddress(): change,257}258self.nodes[2].sendrawtransaction(259self.nodes[2].signrawtransactionwithwallet(260self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict2))['hex'])262# generate on both sides263lastblockhash = self.generate(self.nodes[1], 3, sync_fun=self.no_op)[2]264self.generate(self.nodes[2], 4, sync_fun=self.no_op)266self.join_network()268self.sync_all()270# gettransaction should work for txid1271assert self.nodes[0].gettransaction(txid1)['txid'] == txid1, "gettransaction failed to find txid1"273# listsinceblock(lastblockhash) should now include txid1, as seen from nodes[0]274lsbres = self.nodes[0].listsinceblock(lastblockhash)275assert txid1 in [tx['txid'] for tx in lsbres['removed']]277# but it should not include 'removed' if include_removed=false278lsbres2 = self.nodes[0].listsinceblock(blockhash=lastblockhash, include_removed=False)279assert 'removed' not in lsbres2281def test_double_send(self):282'''283This tests the case where the same transaction is submitted twice on two284separate blocks as part of a reorg. The former will vanish and the285latter will appear as the true transaction (with confirmations dropping286as a result).288ab0289/ \290aa1 [tx1] bb1291| |292aa2 bb2293| |294aa3 bb3 [tx1]295|296bb4298Asserted:3001. tx1 is listed in listsinceblock.3012. It is included in 'removed' as it was removed, even though it is now302present in a different block.3033. It is listed with a confirmation count of 2 (bb3, bb4), not3043 (aa1, aa2, aa3).305'''306self.log.info("Test double send")308self.sync_all()310# Split network into two311self.split_network()313# create and sign a transaction314utxos = self.nodes[2].listunspent()315utxo = utxos[0]316change = '%.8f' % (float(utxo['amount']) - 1.0003)317recipient_dict = {318self.nodes[0].getnewaddress(): 1,319self.nodes[2].getnewaddress(): change,320}321utxo_dicts = [{322'txid': utxo['txid'],323'vout': utxo['vout'],324}]325signedtxres = self.nodes[2].signrawtransactionwithwallet(326self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict))327assert signedtxres['complete']329signedtx = signedtxres['hex']331# send from nodes[1]; this will end up in aa1332txid1 = self.nodes[1].sendrawtransaction(signedtx)334# generate bb1-bb2 on right side335self.generate(self.nodes[2], 2, sync_fun=self.no_op)337# send from nodes[2]; this will end up in bb3338txid2 = self.nodes[2].sendrawtransaction(signedtx)340assert_equal(txid1, txid2)342# generate on both sides343lastblockhash = self.generate(self.nodes[1], 3, sync_fun=self.no_op)[2]344self.generate(self.nodes[2], 2, sync_fun=self.no_op)346self.join_network()348self.sync_all()350# gettransaction should work for txid1351tx1 = self.nodes[0].gettransaction(txid1)352assert_equal(tx1['blockheight'], self.nodes[0].getblockheader(tx1['blockhash'])['height'])354# listsinceblock(lastblockhash) should now include txid1 in transactions355# as well as in removed356lsbres = self.nodes[0].listsinceblock(lastblockhash)357assert txid1 in [tx['txid'] for tx in lsbres['transactions']]358assert txid1 in [tx['txid'] for tx in lsbres['removed']]360# find transaction and ensure confirmations is valid361for tx in lsbres['transactions']:362if tx['txid'] == txid1:363assert_equal(tx['confirmations'], 2)365# the same check for the removed array; confirmations should STILL be 2366for tx in lsbres['removed']:367if tx['txid'] == txid1:368assert_equal(tx['confirmations'], 2)370def double_spends_filtered(self):371'''372`listsinceblock` was returning conflicted transactions even if they373occurred before the specified cutoff blockhash374'''375self.log.info("Test spends filtered")376spending_node = self.nodes[2]377dest_address = spending_node.getnewaddress()379tx_input = dict(380sequence=MAX_BIP125_RBF_SEQUENCE, **next(u for u in spending_node.listunspent()))381rawtx = spending_node.createrawtransaction(382[tx_input], {dest_address: tx_input["amount"] - Decimal("0.00051000"),383spending_node.getrawchangeaddress(): Decimal("0.00050000")})384signedtx = spending_node.signrawtransactionwithwallet(rawtx)385orig_tx_id = spending_node.sendrawtransaction(signedtx["hex"])386original_tx = spending_node.gettransaction(orig_tx_id)388double_tx = spending_node.bumpfee(orig_tx_id)390# check that both transactions exist391block_hash = spending_node.listsinceblock(392spending_node.getblockhash(spending_node.getblockcount()))393original_found = False394double_found = False395for tx in block_hash['transactions']:396if tx['txid'] == original_tx['txid']:397original_found = True398if tx['txid'] == double_tx['txid']:399double_found = True400assert_equal(original_found, True)401assert_equal(double_found, True)403lastblockhash = self.generate(spending_node, 1)[0]405# check that neither transaction exists406block_hash = spending_node.listsinceblock(lastblockhash)407original_found = False408double_found = False409for tx in block_hash['transactions']:410if tx['txid'] == original_tx['txid']:411original_found = True412if tx['txid'] == double_tx['txid']:413double_found = True414assert_equal(original_found, False)415assert_equal(double_found, False)417def test_desc(self):418"""Make sure we can track coins by descriptor."""419self.log.info("Test descriptor lookup by scriptPubKey.")421# Create a watchonly wallet tracking two multisig descriptors.422multi_a = descsum_create("wsh(multi(1,tpubD6NzVbkrYhZ4YBNjUo96Jxd1u4XKWgnoc7LsA1jz3Yc2NiDbhtfBhaBtemB73n9V5vtJHwU6FVXwggTbeoJWQ1rzdz8ysDuQkpnaHyvnvzR/*,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*))")423multi_b = descsum_create("wsh(multi(1,tpubD6NzVbkrYhZ4YHdDGMAYGaWxMSC1B6tPRTHuU5t3BcfcS3nrF523iFm5waFd1pP3ZvJt4Jr8XmCmsTBNx5suhcSgtzpGjGMASR3tau1hJz4/*,tpubD6NzVbkrYhZ4Y2RLiuEzNQkntjmsLpPYDm3LTRBYynUQtDtpzeUKAcb9sYthSFL3YR74cdFgF5mW8yKxv2W2CWuZDFR2dUpE5PF9kbrVXNZ/*))")424self.nodes[0].createwallet(wallet_name="wo", disable_private_keys=True)425wo_wallet = self.nodes[0].get_wallet_rpc("wo")426wo_wallet.importdescriptors([427{428"desc": multi_a,429"active": False,430"timestamp": "now",431},432{433"desc": multi_b,434"active": False,435"timestamp": "now",436},437])439# Send a coin to each descriptor.440assert_equal(len(wo_wallet.listsinceblock()["transactions"]), 0)441addr_a = self.nodes[0].deriveaddresses(multi_a, 0)[0]442addr_b = self.nodes[0].deriveaddresses(multi_b, 0)[0]443self.nodes[2].sendtoaddress(addr_a, 1)444self.nodes[2].sendtoaddress(addr_b, 2)445self.generate(self.nodes[2], 1)447# We can identify on which descriptor each coin was received.448coins = wo_wallet.listsinceblock()["transactions"]449assert_equal(len(coins), 2)450coin_a = next(c for c in coins if c["amount"] == 1)451assert_equal(coin_a["parent_descs"][0], multi_a)452coin_b = next(c for c in coins if c["amount"] == 2)453assert_equal(coin_b["parent_descs"][0], multi_b)455def test_send_to_self(self):456"""We can make listsinceblock output our change outputs."""457self.log.info("Test the inclusion of change outputs in the output.")459# Create a UTxO paying to one of our change addresses.460block_hash = self.nodes[2].getbestblockhash()461addr = self.nodes[2].getrawchangeaddress()462self.nodes[2].sendtoaddress(addr, 1)464# If we don't list change, we won't have an entry for it.465coins = self.nodes[2].listsinceblock(blockhash=block_hash)["transactions"]466assert addr not in [c["address"] for c in coins]468# Now if we list change, we'll get both the send (to a change address) and469# the actual change.470res = self.nodes[2].listsinceblock(blockhash=block_hash, include_change=True)471coins = [entry for entry in res["transactions"] if entry["category"] == "receive"]472assert_equal(len(coins), 2)473assert addr in [c["address"] for c in coins]474assert all(self.nodes[2].getaddressinfo(c["address"])["ischange"] for c in coins)476def test_op_return(self):477"""Test if OP_RETURN outputs will be displayed correctly."""478block_hash = self.nodes[2].getbestblockhash()480raw_tx = self.nodes[2].createrawtransaction([], [{'data': 'aa'}])481funded_tx = self.nodes[2].fundrawtransaction(raw_tx)482signed_tx = self.nodes[2].signrawtransactionwithwallet(funded_tx['hex'])483tx_id = self.nodes[2].sendrawtransaction(signed_tx['hex'])485op_ret_tx = [tx for tx in self.nodes[2].listsinceblock(blockhash=block_hash)["transactions"] if tx['txid'] == tx_id][0]487assert 'address' not in op_ret_tx489def test_label(self):490self.log.info('Test passing "label" argument fetches incoming transactions having the specified label')491new_addr = self.nodes[1].getnewaddress(label="new_addr", address_type="bech32")493self.nodes[2].sendtoaddress(address=new_addr, amount="0.001")494self.generate(self.nodes[2], 1)496for label in ["new_addr", ""]:497new_addr_transactions = self.nodes[1].listsinceblock(label=label)["transactions"]498assert_equal(len(new_addr_transactions), 1)499assert_equal(new_addr_transactions[0]["label"], label)500if label == "new_addr":
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.