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.
252# Higher fee, but the fee per KB is much lower, so the replacement is253# rejected.254tx1b_hex = self.wallet.create_self_transfer_multi(255utxos_to_spend=[tx0_outpoint],256sequence=0,257num_outputs=100,258amount_per_output=1000,259)["hex"]261# This will raise an exception due to insufficient fee262assert_raises_rpc_error(-26, "does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx1b_hex, 0)264def test_spends_of_conflicting_outputs(self):265"""Replacements that spend conflicting tx outputs are rejected"""266utxo1 = self.make_utxo(self.nodes[0], int(1.2 * COIN))267utxo2 = self.make_utxo(self.nodes[0], 3 * COIN)269tx1a = self.wallet.send_self_transfer(270from_node=self.nodes[0],271utxo_to_spend=utxo1,272sequence=0,273fee=Decimal("0.1"),274)275tx1a_utxo = tx1a["new_utxo"]277# Direct spend an output of the transaction we're replacing.278tx2 = self.wallet.create_self_transfer_multi(279utxos_to_spend=[utxo1, utxo2, tx1a_utxo],280sequence=0,281amount_per_output=int(COIN * tx1a_utxo["value"]),282)["tx"]283tx2_hex = tx2.serialize().hex()285# This will raise an exception286reject_reason = "bad-txns-spends-conflicting-tx"287reject_details = f"{reject_reason}, {tx2.txid_hex} spends conflicting transaction {tx1a['tx'].txid_hex}"288res = self.nodes[0].testmempoolaccept(rawtxs=[tx2_hex])[0]289assert_equal(res["reject-reason"], reject_reason)290assert_equal(res["reject-details"], reject_details)291assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, tx2_hex, 0)294# Spend tx1a's output to test the indirect case.295tx1b_utxo = self.wallet.send_self_transfer(296from_node=self.nodes[0],297utxo_to_spend=tx1a_utxo,298sequence=0,299fee=Decimal("0.1"),300)["new_utxo"]302tx2_hex = self.wallet.create_self_transfer_multi(303utxos_to_spend=[utxo1, utxo2, tx1b_utxo],304sequence=0,305amount_per_output=int(COIN * tx1a_utxo["value"]),306)["hex"]308# This will raise an exception309assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)311def test_new_unconfirmed_inputs(self):312"""Replacements that add new unconfirmed inputs may be accepted"""313confirmed_utxo = self.make_utxo(self.nodes[0], int(1.1 * COIN))314unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.2 * COIN), confirmed=False)316self.wallet.send_self_transfer(317from_node=self.nodes[0],318utxo_to_spend=confirmed_utxo,319sequence=0,320fee=Decimal("0.1"),321)323tx2 = self.wallet.create_self_transfer_multi(324utxos_to_spend=[confirmed_utxo, unconfirmed_utxo],325sequence=0,326amount_per_output=1 * COIN,327)["tx"]328tx2_hex = tx2.serialize().hex()330# This will not raise an exception331tx2_id = self.nodes[0].sendrawtransaction(tx2_hex, 0)332assert tx2_id in self.nodes[0].getrawmempool()334def test_new_unconfirmed_input_with_low_feerate(self):335"""Replacements that add new unconfirmed inputs are allowed, but must pass the feerate diagram check"""336confirmed_utxos = [self.make_utxo(self.nodes[0], int(1.1 * COIN)) for _ in range(3)]337large_low_feerate = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxos[0], target_vsize=10000, fee=Decimal("0.00001000"))338self.nodes[0].sendrawtransaction(large_low_feerate['hex'])339unconfirmed_utxo = large_low_feerate['new_utxo']341# These two transactions are approximately the same size. The replacement tx pays twice the fee.342tx_to_replace = self.wallet.create_self_transfer_multi(utxos_to_spend=[confirmed_utxos[1], confirmed_utxos[2]], fee_per_output=2000)343tx_replacement = self.wallet.create_self_transfer_multi(utxos_to_spend=[confirmed_utxos[1], unconfirmed_utxo], fee_per_output=4000)344assert_greater_than(tx_replacement['fee']*tx_to_replace['tx'].get_vsize(), tx_to_replace['fee']*tx_replacement['tx'].get_vsize())346self.nodes[0].sendrawtransaction(tx_to_replace['hex'])347assert_raises_rpc_error(-26, "insufficient feerate: does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx_replacement['hex'])350def test_too_many_replacements(self):351"""Replacements that conflict with too many clusters are rejected"""352# Try directly replacing transactions in more than MAX_REPLACEMENT_LIMIT353# distinct clusters355# Start by creating a single transaction with many outputs356initial_nValue = 10 * COIN357utxo = self.make_utxo(self.nodes[0], initial_nValue)358fee = int(0.0001 * COIN)359split_value = int((initial_nValue - fee) / (MAX_REPLACEMENT_LIMIT + 1))361splitting_tx_utxos = self.wallet.send_self_transfer_multi(362from_node=self.nodes[0],363utxos_to_spend=[utxo],364sequence=0,365num_outputs=MAX_REPLACEMENT_LIMIT + 1,366amount_per_output=split_value,367)["new_utxos"]369self.generate(self.nodes[0], 1)371# Now spend each of those outputs individually372for utxo in splitting_tx_utxos:373self.wallet.send_self_transfer(374from_node=self.nodes[0],375utxo_to_spend=utxo,376sequence=0,377fee=Decimal(fee) / COIN,378)380# Now create doublespend of the whole lot; should fail.381# Need a big enough fee to cover all spending transactions and have382# a higher fee rate383double_spend_value = (split_value - 100 * fee) * (MAX_REPLACEMENT_LIMIT + 1)384double_tx = self.wallet.create_self_transfer_multi(385utxos_to_spend=splitting_tx_utxos,386sequence=0,387amount_per_output=double_spend_value,388)["tx"]389double_tx_hex = double_tx.serialize().hex()391# This will raise an exception392reject_reason = "too many potential replacements"393reject_details = f"{reject_reason}, rejecting replacement {double_tx.txid_hex}; too many conflicting clusters ({MAX_REPLACEMENT_LIMIT + 1} > {MAX_REPLACEMENT_LIMIT})"394res = self.nodes[0].testmempoolaccept(rawtxs=[double_tx_hex])[0]395assert_equal(res["reject-reason"], reject_reason)396assert_equal(res["reject-details"], reject_details)397assert_raises_rpc_error(-26, f"{reject_details}", self.nodes[0].sendrawtransaction, double_tx_hex, 0)400# If we remove an input, it should pass401double_tx.vin.pop()402double_tx_hex = double_tx.serialize().hex()403self.nodes[0].sendrawtransaction(double_tx_hex, 0)405def test_prioritised_transactions(self):406# Ensure that fee deltas used via prioritisetransaction are407# correctly used by replacement logic409# 1. Check that feeperkb uses modified fees410tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))412tx1a_txid = self.wallet.send_self_transfer(413from_node=self.nodes[0],414utxo_to_spend=tx0_outpoint,415sequence=0,416fee=Decimal("0.1"),417)["txid"]419# Higher fee, but the actual fee per KB is much lower.420tx1b_hex = self.wallet.create_self_transfer_multi(421utxos_to_spend=[tx0_outpoint],422sequence=0,423num_outputs=100,424amount_per_output=int(0.00001 * COIN),425)["hex"]427# Verify tx1b cannot replace tx1a.428assert_raises_rpc_error(-26, "does not improve feerate diagram", self.nodes[0].sendrawtransaction, tx1b_hex, 0)430# Use prioritisetransaction to set tx1a's fee to 0.431self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))433# Now tx1b should be able to replace tx1a434tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)436assert tx1b_txid in self.nodes[0].getrawmempool()438# 2. Check that absolute fee checks use modified fee.439tx1_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))441# tx2a442self.wallet.send_self_transfer(443from_node=self.nodes[0],444utxo_to_spend=tx1_outpoint,445sequence=0,446fee=Decimal("0.1"),447)449# Lower fee, but we'll prioritise it450tx2b = self.wallet.create_self_transfer(451utxo_to_spend=tx1_outpoint,452sequence=0,453fee=Decimal("0.09"),454)456# Verify tx2b cannot replace tx2a.457assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b["hex"], 0)459# Now prioritise tx2b to have a higher modified fee460self.nodes[0].prioritisetransaction(txid=tx2b["txid"], fee_delta=int(0.1 * COIN))462# tx2b should now be accepted463tx2b_txid = self.nodes[0].sendrawtransaction(tx2b["hex"], 0)465assert tx2b_txid in self.nodes[0].getrawmempool()467def test_rpc(self):468us0 = self.wallet.get_utxo()469ins = [us0]470outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}471rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)472rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)473json0 = self.nodes[0].decoderawtransaction(rawtx0)474json1 = self.nodes[0].decoderawtransaction(rawtx1)475assert_equal(json0["vin"][0]["sequence"], 4294967293)476assert_equal(json1["vin"][0]["sequence"], 4294967295)478if self.is_wallet_compiled():479self.init_wallet(node=0)480rawtx2 = self.nodes[0].createrawtransaction([], outs)481frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})482frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})484json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])485json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])486assert_equal(json0["vin"][0]["sequence"], 4294967293)487assert_equal(json1["vin"][0]["sequence"], 4294967294)489def test_replacement_relay_fee(self):490tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['tx']492# Higher fee, higher feerate, different txid, but the replacement does not provide a relay493# fee conforming to node's `incrementalrelayfee` policy of 100 sat per KB.494assert_equal(self.nodes[0].getmempoolinfo()["incrementalrelayfee"], Decimal("0.000001"))495tx.vout[0].nValue -= 1496assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())498def test_incremental_relay_feerates(self):499self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...")500node = self.nodes[0]
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.