From 4ec30d53eca271ab52fd9ac7b8aef585f572fb4e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Sat, 20 Sep 2025 21:32:41 +0200 Subject: [PATCH 01/13] test: add block 2016 to mock mainnet The next commit requires an additional mainnet block which changes the difficulty. Also fix a few minor mistakes in the test (suite): - rename the create_coinbase retarger_period argument to halving_period. Before bitcoin#31583 this was hardcoded for regtest where these values are the same. - drop unused fees argument from mine helper Finally the CPU miner instructions for generating the alternative mainnet chain are expanded. Github-Pull: #33446 Rebased-From: 4c3c1f42cf705e039751395799240da33ca969bd --- test/functional/data/README.md | 14 +++++++++++--- test/functional/data/mainnet_alt.json | 6 ++++-- test/functional/mining_mainnet.py | 19 +++++++++++++------ test/functional/test_framework/blocktools.py | 4 ++-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/test/functional/data/README.md b/test/functional/data/README.md index bb03422f95f..956394e385c 100644 --- a/test/functional/data/README.md +++ b/test/functional/data/README.md @@ -11,9 +11,10 @@ The alternate mainnet chain was generated as follows: - restart node with a faketime 2 minutes later ```sh -for i in {1..2015} +for i in {1..2016} do - faketime "`date -d @"$(( 1231006505 + $i * 120 ))" +'%Y-%m-%d %H:%M:%S'`" \ + t=$(( 1231006505 + $i * 120 )) + faketime "`date -d @$t +'%Y-%m-%d %H:%M:%S'`" \ bitcoind -connect=0 -nocheckpoints -stopatheight=$i done ``` @@ -21,7 +22,9 @@ done The CPU miner is kept running as follows: ```sh -./minerd --coinbase-addr 1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r --no-stratum --algo sha256d --no-longpoll --scantime 3 --retry-pause 1 +./minerd -u ... -p ... -o http://127.0.0.1:8332 --no-stratum \ + --coinbase-addr 1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r \ + --algo sha256d --no-longpoll --scantime 3 --retry-pause 1 ``` The payout address is derived from first BIP32 test vector master key: @@ -40,3 +43,8 @@ The timestamp was not kept constant because at difficulty 1 it's not sufficient to only grind the nonce. Grinding the extra_nonce or version field instead would have required additional (stratum) software. It would also make it more complicated to reconstruct the blocks in this test. + +The `getblocktemplate` RPC code needs to be patched to ignore not being connected +to any peers, and to ignore the IBD status check. + +On macOS use `faketime "@$t"` instead. diff --git a/test/functional/data/mainnet_alt.json b/test/functional/data/mainnet_alt.json index a4a072d2c5b..96821a36f41 100644 --- a/test/functional/data/mainnet_alt.json +++ b/test/functional/data/mainnet_alt.json @@ -2014,7 +2014,8 @@ 1231247971, 1231248071, 1231248198, - 1231248322 + 1231248322, + 1231248621 ], "nonces": [ 2345621585, @@ -4031,6 +4032,7 @@ 3658502865, 2519048297, 1915965760, - 1183846025 + 1183846025, + 2713372123 ] } diff --git a/test/functional/mining_mainnet.py b/test/functional/mining_mainnet.py index dbe6248fd74..eafa991cfe2 100755 --- a/test/functional/mining_mainnet.py +++ b/test/functional/mining_mainnet.py @@ -53,15 +53,15 @@ class MiningMainnetTest(BitcoinTestFramework): help='Block data file (default: %(default)s)', ) - def mine(self, height, prev_hash, blocks, node, fees=0): + def mine(self, height, prev_hash, blocks, node): self.log.debug(f"height={height}") block = CBlock() block.nVersion = 0x20000000 block.hashPrevBlock = int(prev_hash, 16) block.nTime = blocks['timestamps'][height - 1] - block.nBits = DIFF_1_N_BITS + block.nBits = DIFF_1_N_BITS if height < 2016 else DIFF_4_N_BITS block.nNonce = blocks['nonces'][height - 1] - block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), retarget_period=2016)] + block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), halving_period=210000)] # The alternate mainnet chain was mined with non-timelocked coinbase txs. block.vtx[0].nLockTime = 0 block.vtx[0].vin[0].nSequence = SEQUENCE_FINAL @@ -82,12 +82,15 @@ class MiningMainnetTest(BitcoinTestFramework): self.log.info("Load alternative mainnet blocks") path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile) prev_hash = node.getbestblockhash() + blocks = None with open(path, encoding='utf-8') as f: blocks = json.load(f) n_blocks = len(blocks['timestamps']) - assert_equal(n_blocks, 2015) - for i in range(2015): - prev_hash = self.mine(i + 1, prev_hash, blocks, node) + assert_equal(n_blocks, 2016) + + # Mine up to the last block of the first retarget period + for i in range(2015): + prev_hash = self.mine(i + 1, prev_hash, blocks, node) assert_equal(node.getblockcount(), 2015) @@ -102,5 +105,9 @@ class MiningMainnetTest(BitcoinTestFramework): assert_equal(mining_info['next']['bits'], nbits_str(DIFF_4_N_BITS)) assert_equal(mining_info['next']['target'], target_str(DIFF_4_TARGET)) + # Mine first block of the second retarget period + height = 2016 + prev_hash = self.mine(height, prev_hash, blocks, node) + assert_equal(node.getblockcount(), height) if __name__ == '__main__': MiningMainnetTest(__file__).main() diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index fa4182c6cf9..eb1d3b0542b 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -144,7 +144,7 @@ def script_BIP34_coinbase_height(height): return CScript([CScriptNum(height)]) -def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50, retarget_period=REGTEST_RETARGET_PERIOD): +def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50, halving_period=REGTEST_RETARGET_PERIOD): """Create a coinbase transaction. If pubkey is passed in, the coinbase output will be a P2PK output; @@ -158,7 +158,7 @@ def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_scr coinbaseoutput = CTxOut() coinbaseoutput.nValue = nValue * COIN if nValue == 50: - halvings = int(height / retarget_period) + halvings = int(height / halving_period) coinbaseoutput.nValue >>= halvings coinbaseoutput.nValue += fees if pubkey is not None: From 1e348bc55a821780630608e6bb936eaebf96db54 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Sat, 20 Sep 2025 21:33:13 +0200 Subject: [PATCH 02/13] rpc: fix getblock(header) returns target for tip A target field was added to the getblock and getblockheader RPC calls in bitcoin#31583, but it mistakingly always used the tip value. Because regtest does not have difficulty adjustment, a test is added for mainnet instead. Github-Pull: #33446 Rebased-From: bf7996cbc3becf329d8b1cd2f1007fec9b3a3188 --- src/rpc/blockchain.cpp | 2 +- test/functional/mining_mainnet.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bd5deedf6ee..4ef863a4805 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -166,7 +166,7 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex result.pushKV("mediantime", blockindex.GetMedianTimePast()); result.pushKV("nonce", blockindex.nNonce); result.pushKV("bits", strprintf("%08x", blockindex.nBits)); - result.pushKV("target", GetTarget(tip, pow_limit).GetHex()); + result.pushKV("target", GetTarget(blockindex, pow_limit).GetHex()); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex.nChainWork.GetHex()); result.pushKV("nTx", blockindex.nTx); diff --git a/test/functional/mining_mainnet.py b/test/functional/mining_mainnet.py index eafa991cfe2..672191006d2 100755 --- a/test/functional/mining_mainnet.py +++ b/test/functional/mining_mainnet.py @@ -109,5 +109,17 @@ class MiningMainnetTest(BitcoinTestFramework): height = 2016 prev_hash = self.mine(height, prev_hash, blocks, node) assert_equal(node.getblockcount(), height) + + mining_info = node.getmininginfo() + assert_equal(mining_info['difficulty'], 4) + + self.log.info("getblock RPC should show historical target") + block_info = node.getblock(node.getblockhash(1)) + + assert_equal(block_info['difficulty'], 1) + assert_equal(block_info['bits'], nbits_str(DIFF_1_N_BITS)) + assert_equal(block_info['target'], target_str(DIFF_1_TARGET)) + + if __name__ == '__main__': MiningMainnetTest(__file__).main() From 45703931e5290dbae44b080394550234489fb704 Mon Sep 17 00:00:00 2001 From: ismaelsadeeq Date: Wed, 24 Sep 2025 16:31:38 +0200 Subject: [PATCH 03/13] miner: fix `addPackageTxs` unsigned integer overflow Github-Pull: #33475 Rebased-From: b807dfcdc5929c314d43b790c9e705d5bf0a86e8 --- src/node/miner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/miner.cpp b/src/node/miner.cpp index a08c70e29da..e75bc3a603b 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -397,8 +397,8 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda ++nConsecutiveFailed; - if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight > - m_options.nBlockMaxWeight - BLOCK_FULL_ENOUGH_WEIGHT_DELTA) { + if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight + + BLOCK_FULL_ENOUGH_WEIGHT_DELTA > m_options.nBlockMaxWeight) { // Give up if we're close to full and haven't succeeded in a while break; } From b75afaccb8b83890b416af9b54711683493d0f89 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Fri, 26 Sep 2025 19:25:26 +0200 Subject: [PATCH 04/13] doc: rpc: fix case typo in `finalizepsbt` help (final_scriptwitness) Github-Pull: #33484 Rebased-From: ff05bebcc4262966b117082a67dc4c63a3f67d2d --- src/rpc/rawtransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b7f458ea294..1a8edc594fe 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1608,7 +1608,7 @@ static RPCHelpMan finalizepsbt() return RPCHelpMan{"finalizepsbt", "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n" "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n" - "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n" + "created which has the final_scriptSig and final_scriptwitness fields filled for inputs that are complete.\n" "Implements the Finalizer and Extractor roles.\n", { {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}, From fce1c607708871b34f58961897b862ecac12ec99 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Sep 2025 23:46:32 +1000 Subject: [PATCH 05/13] datacarrier: Undeprecate configuration option Reverts commit 0b4048c73385166144d0b3e76beb9a2ac4cc1eca Github-Pull: #33453 Rebased-From: 451ba9ada41f687c0e4bb34d5925374a68a8f8a3 --- src/init.cpp | 8 ++------ test/functional/mempool_datacarrier.py | 12 ------------ 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 7c858242a8a..02e5f2a2488 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -658,9 +658,9 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-dustrelayfee=", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); - argsman.AddArg("-datacarrier", strprintf("(DEPRECATED) Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarriersize", - strprintf("(DEPRECATED) Relay and mine transactions whose data-carrying raw scriptPubKeys in aggregate " + strprintf("Relay and mine transactions whose data-carrying raw scriptPubKeys in aggregate " "are of this size or less, allowing multiple outputs (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); @@ -903,10 +903,6 @@ bool AppInitParameterInteraction(const ArgsManager& args) InitWarning(_("Option '-checkpoints' is set but checkpoints were removed. This option has no effect.")); } - if (args.IsArgSet("-datacarriersize") || args.IsArgSet("-datacarrier")) { - InitWarning(_("Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.")); - } - // We no longer limit the orphanage based on number of transactions but keep the option to warn users who still have it in their config. if (args.IsArgSet("-maxorphantx")) { InitWarning(_("Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration.")); diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py index 40d3d2b3ab9..ca113fcbceb 100755 --- a/test/functional/mempool_datacarrier.py +++ b/test/functional/mempool_datacarrier.py @@ -100,17 +100,5 @@ class DataCarrierTest(BitcoinTestFramework): self.test_null_data_transaction(node=self.nodes[2], data=one_byte, success=True) self.test_null_data_transaction(node=self.nodes[3], data=one_byte, success=False) - # Clean shutdown boilerplate due to deprecation - self.expected_stderr = [ - "", # node 0 has no deprecated options - "Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.", - "Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.", - "Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.", - ] - - for i in range(self.num_nodes): - self.stop_node(i, expected_stderr=self.expected_stderr[i]) - - if __name__ == '__main__': DataCarrierTest(__file__).main() From a3a1dcb589e417609ae29121e50bb61633e125bd Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Mon, 29 Sep 2025 15:47:07 -0400 Subject: [PATCH 06/13] fuzz: don't bypass_limits for most mempool harnesses Using bypass_limits=true is essentially fuzzing part of a reorg only, and results in TRUC invariants unable to be checked. Remove most instances of bypassing limits, leaving one harness able to do so. Github-Pull: #33504 Rebased-From: bbe8e9063c15dc230553e0cbf16d603f5ad0e4cf --- src/test/fuzz/package_eval.cpp | 2 +- src/test/fuzz/tx_pool.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp index 23ea9d1f15a..50f4c416d79 100644 --- a/src/test/fuzz/package_eval.cpp +++ b/src/test/fuzz/package_eval.cpp @@ -325,7 +325,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool) return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{})); const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(), - /*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit)); + /*bypass_limits=*/false, /*test_accept=*/!single_submit)); if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) { // We don't know anything about the validity since transactions were randomly generated, so diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index 4245d3e1246..1db32a6dadc 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -296,7 +296,6 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool) std::set added; auto txr = std::make_shared(removed, added); node.validation_signals->RegisterSharedValidationInterface(txr); - const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); // Make sure ProcessNewPackage on one transaction works. // The result is not guaranteed to be the same as what is returned by ATMP. @@ -311,7 +310,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool) it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); } - const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); + const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false)); const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->UnregisterSharedValidationInterface(txr); @@ -394,6 +393,9 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool) chainstate.SetMempool(&tx_pool); + // If we ever bypass limits, do not do TRUC invariants checks + bool ever_bypassed_limits{false}; + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300) { const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids); @@ -412,13 +414,17 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool) tx_pool.PrioritiseTransaction(txid, delta); } + const bool bypass_limits{fuzzed_data_provider.ConsumeBool()}; + ever_bypassed_limits |= bypass_limits; + const auto tx = MakeTransactionRef(mut_tx); - const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; if (accepted) { txids.push_back(tx->GetHash()); - CheckMempoolTRUCInvariants(tx_pool); + if (!ever_bypassed_limits) { + CheckMempoolTRUCInvariants(tx_pool); + } } } Finish(fuzzed_data_provider, tx_pool, chainstate); From 348525258435ec84c20281a78fda4ad1b5a5565e Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Fri, 26 Sep 2025 14:47:47 -0400 Subject: [PATCH 07/13] Mempool: Do not enforce TRUC checks on reorg Not enforcing TRUC topology on reorg was the intended behavior, but the appropriate bypass argument was not checked. This mistake means we could potentially invalidate a long chain of perfectly incentive-compatible transactions that were made historically, including subsequent non-TRUC transactions, all of which may have been very high feerate. Lastly, it wastes CPU cycles doing topology checks since this behavior cannot actually enforce the topology in general for the reorg setting. Github-Pull: #33504 Rebased-From: 26e71c237d9d2197824b547f55ee3a0a60149f92 --- src/validation.cpp | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 8fcc719a684..3f77955da62 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1044,26 +1044,28 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Even though just checking direct mempool parents for inheritance would be sufficient, we // check using the full ancestor set here because it's more convenient to use what we have // already calculated. - if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { - // Single transaction contexts only. - if (args.m_allow_sibling_eviction && err->second != nullptr) { - // We should only be considering where replacement is considered valid as well. - Assume(args.m_allow_replacement); + if (!args.m_bypass_limits) { + if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { + // Single transaction contexts only. + if (args.m_allow_sibling_eviction && err->second != nullptr) { + // We should only be considering where replacement is considered valid as well. + Assume(args.m_allow_replacement); - // Potential sibling eviction. Add the sibling to our list of mempool conflicts to be - // included in RBF checks. - ws.m_conflicts.insert(err->second->GetHash()); - // Adding the sibling to m_iters_conflicting here means that it doesn't count towards - // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from - // the descendant count is done separately in SingleTRUCChecks for TRUC transactions. - ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value()); - ws.m_sibling_eviction = true; - // The sibling will be treated as part of the to-be-replaced set in ReplacementChecks. - // Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC - // (which is normally done in PreChecks). However, the only way a TRUC transaction can - // have a non-TRUC and non-BIP125 descendant is due to a reorg. - } else { - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first); + // Potential sibling eviction. Add the sibling to our list of mempool conflicts to be + // included in RBF checks. + ws.m_conflicts.insert(err->second->GetHash()); + // Adding the sibling to m_iters_conflicting here means that it doesn't count towards + // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from + // the descendant count is done separately in SingleTRUCChecks for TRUC transactions. + ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value()); + ws.m_sibling_eviction = true; + // The sibling will be treated as part of the to-be-replaced set in ReplacementChecks. + // Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC + // (which is normally done in PreChecks). However, the only way a TRUC transaction can + // have a non-TRUC and non-BIP125 descendant is due to a reorg. + } else { + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first); + } } } From e4f9ec2f05bd28f9cfc35a0d914f3772c1c7666c Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Fri, 26 Sep 2025 14:49:06 -0400 Subject: [PATCH 08/13] test: add more TRUC reorg coverge Github-Pull: #33504 Rebased-From: 06df14ba75be5f48cf9c417424900ace17d1cf4d --- test/functional/mempool_truc.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/test/functional/mempool_truc.py b/test/functional/mempool_truc.py index 562afcd34dd..098631b5c41 100755 --- a/test/functional/mempool_truc.py +++ b/test/functional/mempool_truc.py @@ -165,23 +165,36 @@ class MempoolTRUC(BitcoinTestFramework): def test_truc_reorg(self): node = self.nodes[0] self.log.info("Test that, during a reorg, TRUC rules are not enforced") - tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2) - tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3) - tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3) - self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"]]) + self.check_mempool([]) + + # Testing 2<-3 versions allowed + tx_v2_block = self.wallet.create_self_transfer(version=2) + + # Testing 3<-2 versions allowed + tx_v3_block = self.wallet.create_self_transfer(version=3) + + # Testing overly-large child size + tx_v3_block2 = self.wallet.create_self_transfer(version=3) + + # Also create a linear chain of 3 TRUC transactions that will be directly mined, followed by one v2 in-mempool after block is made + tx_chain_1 = self.wallet.create_self_transfer(version=3) + tx_chain_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_1["new_utxo"], version=3) + tx_chain_3 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_2["new_utxo"], version=3) + + tx_to_mine = [tx_v3_block["hex"], tx_v2_block["hex"], tx_v3_block2["hex"], tx_chain_1["hex"], tx_chain_2["hex"], tx_chain_3["hex"]] + block = self.generateblock(node, output="raw(42)", transactions=tx_to_mine) - block = self.generate(node, 1) self.check_mempool([]) tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2) tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3) tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_vsize=1250, version=3) assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], TRUC_CHILD_MAX_VSIZE) - self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]]) - node.invalidateblock(block[0]) - self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]]) - # This is needed because generate() will create the exact same block again. - node.reconsiderblock(block[0]) + tx_chain_4 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_chain_3["new_utxo"], version=2) + self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_4["txid"]]) + # Reorg should have all block transactions re-accepted, ignoring TRUC enforcement + node.invalidateblock(block["hash"]) + self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_1["txid"], tx_chain_2["txid"], tx_chain_3["txid"], tx_chain_4["txid"]]) @cleanup(extra_args=["-limitdescendantsize=10"]) def test_nondefault_package_limits(self): From 1eb578045d295095de95840fcfc800f74c8ca098 Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 19 Sep 2025 13:32:00 +0100 Subject: [PATCH 09/13] depends: static libxcb_cursor Modern Ubuntu isn't shipping with this library installed by default. Staticly link it to remove the need for end-users to install it. Closes #33432. Github-Pull: #33434 Rebased-From: eca50854e1cb04e20478bd3df4762e18520a3611 --- contrib/guix/symbol-check.py | 1 - depends/packages/libxcb_util_cursor.mk | 2 +- doc/build-unix.md | 6 ------ 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/contrib/guix/symbol-check.py b/contrib/guix/symbol-check.py index d808ef6becf..79ab6b0fe54 100755 --- a/contrib/guix/symbol-check.py +++ b/contrib/guix/symbol-check.py @@ -112,7 +112,6 @@ ELF_ALLOWED_LIBRARIES = { 'libfontconfig.so.1', # font support 'libfreetype.so.6', # font parsing 'libdl.so.2', # programming interface to dynamic linker -'libxcb-cursor.so.0', 'libxcb-icccm.so.4', 'libxcb-image.so.0', 'libxcb-shm.so.0', diff --git a/depends/packages/libxcb_util_cursor.mk b/depends/packages/libxcb_util_cursor.mk index b308890638d..288afeaa1f0 100644 --- a/depends/packages/libxcb_util_cursor.mk +++ b/depends/packages/libxcb_util_cursor.mk @@ -6,7 +6,7 @@ $(package)_sha256_hash=0e9c5446dc6f3beb8af6ebfcc9e27bcc6da6fe2860f7fc07b99144dfa $(package)_dependencies=libxcb libxcb_util_render libxcb_util_image define $(package)_set_vars -$(package)_config_opts = --disable-static +$(package)_config_opts = --disable-shared $(package)_config_opts += --disable-dependency-tracking --enable-option-checking endef diff --git a/doc/build-unix.md b/doc/build-unix.md index bfb3bd35d99..d9bbb350a7a 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -81,8 +81,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo sudo apt-get install qt6-base-dev qt6-tools-dev qt6-l10n-tools qt6-tools-dev-tools libgl-dev -For Qt 6.5 and later, the `libxcb-cursor0` package must be installed at runtime. - Additionally, to support Wayland protocol for modern desktop environments: sudo apt install qt6-wayland @@ -133,8 +131,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo sudo dnf install qt6-qtbase-devel qt6-qttools-devel -For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime. - Additionally, to support Wayland protocol for modern desktop environments: sudo dnf install qt6-qtwayland @@ -182,8 +178,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo apk add qt6-qtbase-dev qt6-qttools-dev -For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime. - The GUI will be able to encode addresses in QR codes unless this feature is explicitly disabled. To install libqrencode, run: apk add libqrencode-dev From f957c2171d9667ba133532080f0e1f065bd67593 Mon Sep 17 00:00:00 2001 From: amisha Date: Wed, 10 Sep 2025 21:04:57 +0530 Subject: [PATCH 10/13] contrib: fix using macdploy script without translations. QT translations are optional, but the script would error when 'translations_dir' falls back to its default value NULL. This PR fixes it by moving the set-up of QT translations under the check for 'translations_dir' presence. Github-Pull: #33482 Rebased-From: 7b5261f7ef3d88361204c40eb10c0d9dc44f5ed7 --- contrib/macdeploy/macdeployqtplus | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index ba532d77740..802afa5389d 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -466,18 +466,18 @@ if config.translations_dir: sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n") sys.exit(1) -print("+ Adding Qt translations +") + print("+ Adding Qt translations +") -translations = Path(config.translations_dir[0]) + translations = Path(config.translations_dir[0]) -regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)') + regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)') -lang_files = [x for x in translations.iterdir() if regex.match(x.name)] + lang_files = [x for x in translations.iterdir() if regex.match(x.name)] -for file in lang_files: - if verbose: - print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name)) - shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name)) + for file in lang_files: + if verbose: + print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name)) + shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name)) # ------------------------------------------------ From e4b568917c8a73df30c9ab59575ada0eda8f27bf Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 3 Oct 2025 16:28:31 +0100 Subject: [PATCH 11/13] build: bump version to v30.0rc3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e17aafc3ac8..b78274af2a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ set(CLIENT_NAME "Bitcoin Core") set(CLIENT_VERSION_MAJOR 30) set(CLIENT_VERSION_MINOR 0) set(CLIENT_VERSION_BUILD 0) -set(CLIENT_VERSION_RC 2) +set(CLIENT_VERSION_RC 3) set(CLIENT_VERSION_IS_RELEASE "true") set(COPYRIGHT_YEAR "2025") From a2ac6cce5780b98e6bba6b22eb66765258165cee Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 3 Oct 2025 16:32:40 +0100 Subject: [PATCH 12/13] doc: update manual pages for v30.0rc3 --- doc/man/bitcoin-cli.1 | 6 +++--- doc/man/bitcoin-qt.1 | 14 +++++++------- doc/man/bitcoin-tx.1 | 6 +++--- doc/man/bitcoin-util.1 | 6 +++--- doc/man/bitcoin-wallet.1 | 6 +++--- doc/man/bitcoin.1 | 4 ++-- doc/man/bitcoind.1 | 14 +++++++------- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index b524d0318fa..fcb2ef423e4 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-CLI "1" "September 2025" "bitcoin-cli v30.0.0rc2" "User Commands" +.TH BITCOIN-CLI "1" "October 2025" "bitcoin-cli v30.0.0rc3" "User Commands" .SH NAME -bitcoin-cli \- manual page for bitcoin-cli v30.0.0rc2 +bitcoin-cli \- manual page for bitcoin-cli v30.0.0rc3 .SH SYNOPSIS .B bitcoin-cli [\fI\,options\/\fR] \fI\, \/\fR[\fI\,params\/\fR] @@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v30.0.0rc2 .B bitcoin-cli [\fI\,options\/\fR] \fI\,help \/\fR .SH DESCRIPTION -Bitcoin Core RPC client version v30.0.0rc2 +Bitcoin Core RPC client version v30.0.0rc3 .PP The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server. .PP diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index df313ca2803..67324184dd2 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-QT "1" "September 2025" "bitcoin-qt v30.0.0rc2" "User Commands" +.TH BITCOIN-QT "1" "October 2025" "bitcoin-qt v30.0.0rc3" "User Commands" .SH NAME -bitcoin-qt \- manual page for bitcoin-qt v30.0.0rc2 +bitcoin-qt \- manual page for bitcoin-qt v30.0.0rc3 .SH SYNOPSIS .B bitcoin-qt [\fI\,options\/\fR] [\fI\,URI\/\fR] .SH DESCRIPTION -Bitcoin Core version v30.0.0rc2 +Bitcoin Core version v30.0.0rc3 .PP The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core. .PP @@ -695,13 +695,13 @@ Equivalent bytes per sigop in transactions for relay and mining .HP \fB\-datacarrier\fR .IP -(DEPRECATED) Relay and mine data carrier transactions (default: 1) +Relay and mine data carrier transactions (default: 1) .HP \fB\-datacarriersize\fR .IP -(DEPRECATED) Relay and mine transactions whose data\-carrying raw -scriptPubKeys in aggregate are of this size or less, allowing -multiple outputs (default: 100000) +Relay and mine transactions whose data\-carrying raw scriptPubKeys in +aggregate are of this size or less, allowing multiple outputs +(default: 100000) .HP \fB\-minrelaytxfee=\fR .IP diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index d28d33edaea..1dea8e061f5 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-TX "1" "September 2025" "bitcoin-tx v30.0.0rc2" "User Commands" +.TH BITCOIN-TX "1" "October 2025" "bitcoin-tx v30.0.0rc3" "User Commands" .SH NAME -bitcoin-tx \- manual page for bitcoin-tx v30.0.0rc2 +bitcoin-tx \- manual page for bitcoin-tx v30.0.0rc3 .SH SYNOPSIS .B bitcoin-tx [\fI\,options\/\fR] \fI\, \/\fR[\fI\,commands\/\fR] @@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v30.0.0rc2 .B bitcoin-tx [\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] .SH DESCRIPTION -Bitcoin Core bitcoin\-tx utility version v30.0.0rc2 +Bitcoin Core bitcoin\-tx utility version v30.0.0rc3 .PP The bitcoin\-tx tool is used for creating and modifying bitcoin transactions. .PP diff --git a/doc/man/bitcoin-util.1 b/doc/man/bitcoin-util.1 index 14071e4ac37..c59b3f30ba9 100644 --- a/doc/man/bitcoin-util.1 +++ b/doc/man/bitcoin-util.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-UTIL "1" "September 2025" "bitcoin-util v30.0.0rc2" "User Commands" +.TH BITCOIN-UTIL "1" "October 2025" "bitcoin-util v30.0.0rc3" "User Commands" .SH NAME -bitcoin-util \- manual page for bitcoin-util v30.0.0rc2 +bitcoin-util \- manual page for bitcoin-util v30.0.0rc3 .SH SYNOPSIS .B bitcoin-util [\fI\,options\/\fR] [\fI\,command\/\fR] @@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v30.0.0rc2 .B bitcoin-util [\fI\,options\/\fR] \fI\,grind \/\fR .SH DESCRIPTION -Bitcoin Core bitcoin\-util utility version v30.0.0rc2 +Bitcoin Core bitcoin\-util utility version v30.0.0rc3 .PP The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below. .SH OPTIONS diff --git a/doc/man/bitcoin-wallet.1 b/doc/man/bitcoin-wallet.1 index 7d60722fadf..b040cbae538 100644 --- a/doc/man/bitcoin-wallet.1 +++ b/doc/man/bitcoin-wallet.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN-WALLET "1" "September 2025" "bitcoin-wallet v30.0.0rc2" "User Commands" +.TH BITCOIN-WALLET "1" "October 2025" "bitcoin-wallet v30.0.0rc3" "User Commands" .SH NAME -bitcoin-wallet \- manual page for bitcoin-wallet v30.0.0rc2 +bitcoin-wallet \- manual page for bitcoin-wallet v30.0.0rc3 .SH SYNOPSIS .B bitcoin-wallet [\fI\,options\/\fR] \fI\,\/\fR .SH DESCRIPTION -Bitcoin Core bitcoin\-wallet utility version v30.0.0rc2 +Bitcoin Core bitcoin\-wallet utility version v30.0.0rc3 .PP bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files. .PP diff --git a/doc/man/bitcoin.1 b/doc/man/bitcoin.1 index e7f1a31528f..ba39bbe419d 100644 --- a/doc/man/bitcoin.1 +++ b/doc/man/bitcoin.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIN "1" "September 2025" "bitcoin v30.0.0rc2" "User Commands" +.TH BITCOIN "1" "October 2025" "bitcoin v30.0.0rc3" "User Commands" .SH NAME -bitcoin \- manual page for bitcoin v30.0.0rc2 +bitcoin \- manual page for bitcoin v30.0.0rc3 .SH SYNOPSIS .B bitcoin [\fI\,OPTIONS\/\fR] \fI\,COMMAND\/\fR... diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index 4284cff1d1b..ddc09d82fda 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BITCOIND "1" "September 2025" "bitcoind v30.0.0rc2" "User Commands" +.TH BITCOIND "1" "October 2025" "bitcoind v30.0.0rc3" "User Commands" .SH NAME -bitcoind \- manual page for bitcoind v30.0.0rc2 +bitcoind \- manual page for bitcoind v30.0.0rc3 .SH SYNOPSIS .B bitcoind [\fI\,options\/\fR] .SH DESCRIPTION -Bitcoin Core daemon version v30.0.0rc2 +Bitcoin Core daemon version v30.0.0rc3 .PP The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses. .PP @@ -695,13 +695,13 @@ Equivalent bytes per sigop in transactions for relay and mining .HP \fB\-datacarrier\fR .IP -(DEPRECATED) Relay and mine data carrier transactions (default: 1) +Relay and mine data carrier transactions (default: 1) .HP \fB\-datacarriersize\fR .IP -(DEPRECATED) Relay and mine transactions whose data\-carrying raw -scriptPubKeys in aggregate are of this size or less, allowing -multiple outputs (default: 100000) +Relay and mine transactions whose data\-carrying raw scriptPubKeys in +aggregate are of this size or less, allowing multiple outputs +(default: 100000) .HP \fB\-minrelaytxfee=\fR .IP From 4e869a67aa7415f9c756bf6463e3437ae0a3ec44 Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 3 Oct 2025 16:33:13 +0100 Subject: [PATCH 13/13] doc: update example bitcoin conf for 30.0rc3 --- share/examples/bitcoin.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf index 5ae2853ff57..5eacee9ad94 100644 --- a/share/examples/bitcoin.conf +++ b/share/examples/bitcoin.conf @@ -576,12 +576,12 @@ # (default: 20) #bytespersigop=1 -# (DEPRECATED) Relay and mine data carrier transactions (default: 1) +# Relay and mine data carrier transactions (default: 1) #datacarrier=1 -# (DEPRECATED) Relay and mine transactions whose data-carrying raw -# scriptPubKeys in aggregate are of this size or less, allowing -# multiple outputs (default: 100000) +# Relay and mine transactions whose data-carrying raw scriptPubKeys in +# aggregate are of this size or less, allowing multiple outputs +# (default: 100000) #datacarriersize=1 # Fees (in BTC/kvB) smaller than this are considered zero fee for