backport: Bitcoin Core v0.26 backports (batch 1)#7237
backport: Bitcoin Core v0.26 backports (batch 1)#7237thepastaclaw wants to merge 15 commits intodashpay:developfrom
Conversation
|
…s that exceed the max allowed weight
… txindex This backports Bitcoin PR bitcoin#25939 (commits a5b4883, 6e9f8bb) which: - Extracts PSBT updating logic from utxoupdatepsbt into ProcessPSBT - Makes utxoupdatepsbt look for full transactions in the txindex and mempool before falling back to the UTXO set - Moves RemoveUnnecessaryTransactions from wallet to psbt module Dash adaptations: - Use CoreContext instead of std::any for context parameter - RemoveUnnecessaryTransactions is a no-op (Dash has no segwit) - UTXO set fallback for witness_utxo removed (Dash has no segwit) - Test expectations adapted for non-segwit addresses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… even in packages
…ubtractfeefrom parameter
6d79e83 to
5e41969
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
This batch backport of 13 Bitcoin Core v0.26 PRs is clean and well-adapted for Dash. All SegWit removal, naming, and constructor-signature adaptations are correct. Three minor cleanup items remain from the bitcoin#25939 PSBT commit (dead code), the bitcoin#26720 fuzz commit (missing explicit include), and the bitcoin#26933 packages commit (misleading test function).
Reviewed commit: 5e41969
🟡 2 suggestion(s) | 💬 1 nitpick(s)
2 additional findings
🟡 suggestion: `test_submit_cpfp` no longer exercises CPFP
test/functional/rpc_packages.py (lines 296-326)
Upstream removed this function (CPFP testing moved to mempool_limit.py where the mempool min fee is elevated). The Dash version retains it but sets tx_poor to pay at relayfee (line 302), which is sufficient for individual acceptance — no child fee bumping is needed. tx_rich gets in via prioritisetransaction, not CPFP. The function name is misleading since no CPFP is actually exercised. Real CPFP coverage exists in mempool_limit.py (line 87). Consider removing this function to match upstream, or renaming it to reflect what it actually tests (multi-parent package submission with prioritization).
💬 nitpick: Missing explicit `#include ` for `MAX_STANDARD_TX_SIZE`
src/wallet/test/fuzz/coinselection.cpp (lines 5-6)
The fuzz test uses MAX_STANDARD_TX_SIZE (defined in policy/policy.h) but does not include that header directly. It compiles today via the transitive chain test/fuzz/util.h → txmempool.h → policy/packages.h → policy/policy.h. Upstream explicitly adds the include. Adding it here would match upstream and protect against future include-chain changes, but this is a minor hygiene issue rather than a bug.
💡 Suggested change
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/rpc/rawtransaction.cpp`:
- [SUGGESTION] lines 236-265: Dead `coins` map left behind after SegWit fallback removal
The `coins` map (line 239) is declared, entries are inserted at line 264 for inputs not found in txindex/mempool, but the map is never read. Upstream uses it in a `FindCoins` + `IsSegWitOutput` block that populates `witness_utxo` — that block was correctly removed for Dash (no SegWit), but the map declaration, insertion, and stale comment (lines 236-238 referencing "segwit inputs") were left behind. The explanatory comment at lines 268-270 is good and should stay; the dead `coins` variable and its stale comment should be removed.
In `test/functional/rpc_packages.py`:
- [SUGGESTION] lines 296-326: `test_submit_cpfp` no longer exercises CPFP
Upstream removed this function (CPFP testing moved to `mempool_limit.py` where the mempool min fee is elevated). The Dash version retains it but sets `tx_poor` to pay at `relayfee` (line 302), which is sufficient for individual acceptance — no child fee bumping is needed. `tx_rich` gets in via `prioritisetransaction`, not CPFP. The function name is misleading since no CPFP is actually exercised. Real CPFP coverage exists in `mempool_limit.py` (line 87). Consider removing this function to match upstream, or renaming it to reflect what it actually tests (multi-parent package submission with prioritization).
| // If we can't find the corresponding full transaction for all of our inputs, | ||
| // this will be used to find just the utxos for the segwit inputs for which | ||
| // the full transaction isn't found | ||
| std::map<COutPoint, Coin> coins; | ||
|
|
||
| // Fetch previous transactions: | ||
| // First, look in the txindex and the mempool | ||
| for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { | ||
| PSBTInput& psbt_input = psbtx.inputs.at(i); | ||
| const CTxIn& tx_in = psbtx.tx->vin.at(i); | ||
|
|
||
| // The `non_witness_utxo` is the whole previous transaction | ||
| if (psbt_input.non_witness_utxo) continue; | ||
|
|
||
| CTransactionRef tx; | ||
|
|
||
| // Look in the txindex | ||
| if (g_txindex) { | ||
| uint256 block_hash; | ||
| g_txindex->FindTx(tx_in.prevout.hash, block_hash, tx); | ||
| } | ||
| // If we still don't have it look in the mempool | ||
| if (!tx) { | ||
| tx = node.mempool->get(tx_in.prevout.hash); | ||
| } | ||
| if (tx) { | ||
| psbt_input.non_witness_utxo = tx; | ||
| } else { | ||
| coins[tx_in.prevout]; // Create empty map entry keyed by prevout | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: Dead coins map left behind after SegWit fallback removal
The coins map (line 239) is declared, entries are inserted at line 264 for inputs not found in txindex/mempool, but the map is never read. Upstream uses it in a FindCoins + IsSegWitOutput block that populates witness_utxo — that block was correctly removed for Dash (no SegWit), but the map declaration, insertion, and stale comment (lines 236-238 referencing "segwit inputs") were left behind. The explanatory comment at lines 268-270 is good and should stay; the dead coins variable and its stale comment should be removed.
source: ['claude']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/rpc/rawtransaction.cpp`:
- [SUGGESTION] lines 236-265: Dead `coins` map left behind after SegWit fallback removal
The `coins` map (line 239) is declared, entries are inserted at line 264 for inputs not found in txindex/mempool, but the map is never read. Upstream uses it in a `FindCoins` + `IsSegWitOutput` block that populates `witness_utxo` — that block was correctly removed for Dash (no SegWit), but the map declaration, insertion, and stale comment (lines 236-238 referencing "segwit inputs") were left behind. The explanatory comment at lines 268-270 is good and should stay; the dead `coins` variable and its stale comment should be removed.
There was a problem hiding this comment.
Fixed in thepastaclaw/dash@f984d20b5d (branch fix/review-854-cleanup). Removed the dead coins map declaration, its insertion at line 264, and the stale SegWit comment. The explanatory comment about Dash not supporting the SegWit fallback is preserved.
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
Clean incremental backport of bitcoin#17860 (fuzz: BIP 30, CVE-2018-17144). All Dash adaptations — cs_main locking, GetBlockSubsidy(CBlockIndex*), BlockMerkleRoot instead of RegenerateCommitments — are correctly applied. The MineBlock API refactoring (CTxIn→COutPoint) and LoadVerifyActivateChainstate extraction are properly wired to all callers. One minor locking inconsistency in the fuzz test.
Reviewed commit: 93a2516
🟡 1 suggestion(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/test/fuzz/utxo_total_supply.cpp`:
- [SUGGESTION] line 114: ActiveTip() called without cs_main lock
`GetBlockSubsidy(chainman.ActiveTip(), ...)` is called without holding `::cs_main` at lines 114 and 146. Upstream uses `GetBlockSubsidy(ActiveHeight(), ...)` which goes through the lock-acquiring `ActiveHeight()` lambda defined at line 35. The Dash adaptation correctly changed the argument from `int` to `CBlockIndex*` (matching Dash's `GetBlockSubsidy` signature) but dropped the locking wrapper. Single-threaded fuzz tests won't race in practice, but adding a parallel `ActiveTip()` lambda would match the locking discipline already used 20 lines above and prevent thread-safety annotations from flagging this if they're tightened later.
| } | ||
| current_block->hashMerkleRoot = BlockMerkleRoot(*current_block); | ||
| assert(!MineBlock(node, current_block).IsNull()); | ||
| circulation += GetBlockSubsidy(chainman.ActiveTip(), Params().GetConsensus()); |
There was a problem hiding this comment.
🟡 Suggestion: ActiveTip() called without cs_main lock
GetBlockSubsidy(chainman.ActiveTip(), ...) is called without holding ::cs_main at lines 114 and 146. Upstream uses GetBlockSubsidy(ActiveHeight(), ...) which goes through the lock-acquiring ActiveHeight() lambda defined at line 35. The Dash adaptation correctly changed the argument from int to CBlockIndex* (matching Dash's GetBlockSubsidy signature) but dropped the locking wrapper. Single-threaded fuzz tests won't race in practice, but adding a parallel ActiveTip() lambda would match the locking discipline already used 20 lines above and prevent thread-safety annotations from flagging this if they're tightened later.
source: ['claude']
🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/test/fuzz/utxo_total_supply.cpp`:
- [SUGGESTION] line 114: ActiveTip() called without cs_main lock
`GetBlockSubsidy(chainman.ActiveTip(), ...)` is called without holding `::cs_main` at lines 114 and 146. Upstream uses `GetBlockSubsidy(ActiveHeight(), ...)` which goes through the lock-acquiring `ActiveHeight()` lambda defined at line 35. The Dash adaptation correctly changed the argument from `int` to `CBlockIndex*` (matching Dash's `GetBlockSubsidy` signature) but dropped the locking wrapper. Single-threaded fuzz tests won't race in practice, but adding a parallel `ActiveTip()` lambda would match the locking discipline already used 20 lines above and prevent thread-safety annotations from flagging this if they're tightened later.
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
All findings validated against the source code. The Codex mixed-marker round-trip concern is upstream Bitcoin Core bitcoin#26076 behavior faithfully backported — not a Dash-specific regression. The only real Dash-specific observation is a minor format specifier inconsistency in CoinJoin detection code that has no functional impact due to tinyformat's type-safe handling. No blocking issues found. The backport is clean.
Reviewed commit: c624011
💬 1 nitpick(s)
| std::string match = strprintf("/%dh/%sh/4h/0h", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); | ||
| // Fallback: also match old-format descriptors that used apostrophe marker | ||
| std::string match_legacy = strprintf("/%d'/%s'/4'/0'", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); | ||
| bool is_cj = type != std::nullopt && (descriptor.find(match) != std::string::npos || descriptor.find(match_legacy) != std::string::npos); |
There was a problem hiding this comment.
💬 Nitpick: Format specifier inconsistency: %s used for int ExtCoinType()
Both match and match_legacy use %s for Params().ExtCoinType() which returns int, while BIP32_PURPOSE_FEATURE (also integral) uses %d in the same format string. Elsewhere in the codebase (scriptpubkeyman.cpp:2156), ExtCoinType() uses %d. This is purely cosmetic — tinyformat is type-safe and handles it correctly — but %d would be more consistent.
💡 Suggested change
| std::string match = strprintf("/%dh/%sh/4h/0h", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); | |
| // Fallback: also match old-format descriptors that used apostrophe marker | |
| std::string match_legacy = strprintf("/%d'/%s'/4'/0'", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); | |
| bool is_cj = type != std::nullopt && (descriptor.find(match) != std::string::npos || descriptor.find(match_legacy) != std::string::npos); | |
| std::string match = strprintf("/%dh/%dh/4h/0h", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); | |
| std::string match_legacy = strprintf("/%d'/%d'/4'/0'", BIP32_PURPOSE_FEATURE, Params().ExtCoinType()); |
source: ['claude']
60c4a65 to
946d202
Compare
… loadblock import
…ate for other peers
…o BlockManager methods
…s in rpc_psbt.py (#9) * Merge bitcoin#27325: test: various `converttopsbt` check cleanups in rpc_psbt.py afc2dd5 test: various `converttopsbt` check cleanups in rpc_psbt.py (Sebastian Falbesoner) Pull request description: In the functional test rpc_psbt.py, some comments around the `converttopsbt` RPC checks are wrong or outdated and can be removed: > _Error could be either "TX decode failed" (segwit inputs causes > parsing to fail) or "Inputs must not have scriptSigs and > scriptWitnesses"_ Decoding a valid TX with at least one input always succeeds with the [heuristic](https://github.com/bitcoin/bitcoin/blob/e352f5ab6b60ec1cc549997275e945238508cdee/src/core_read.cpp#L126), i.e. this comment is not right and we can assert for the error string "Inputs must not have scriptSigs and scriptWitnesses" on the calls below. > _We must set iswitness=True because the serialized transaction has > inputs and is therefore a witness transaction_ This is also unneeded (and confusing, w.r.t. "is therefore a witness transaction"?), for a TX with one input there is no need to set the `iswitness` parameter. For sake of completeness, we still keep one variant where iswitness is explicitly set to true. Lastly, there is a superflous `converttopsbt` call on the raw tx which is the same as just [about ~10 lines above](https://github.com/bitcoin/bitcoin/blob/master/test/functional/rpc_psbt.py#L393-L397), so it can be removed. ACKs for top commit: ismaelsadeeq: tested ACK afc2dd5 achow101: ACK afc2dd5 Tree-SHA512: 467efefdb3f61efdb79145044b90fc8dc2f0c425f078117a99112b0074e7d4a32c34e464f665fbf8de70d06caaa5d4ad5908c1d75d2e7607eccb0837480afab3 * fix: remove iswitness test case — Dash converttopsbt does not support iswitness parameter --------- Co-authored-by: Andrew Chow <github@achow101.com> Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
…ck exceeds blockfile size (#5) * Merge bitcoin#27191: blockstorage: Adjust fastprune limit if block exceeds blockfile size 8f14fc8 test: cover fastprune with excessive block size (Matthew Zipkin) 271c23e blockstorage: Adjust fastprune limit if block exceeds blockfile size (Martin Zumsande) Pull request description: The debug-only `-fastprune` option used in several tests is not always safe to use: If a `-fastprune` node receives a block larger than the maximum blockfile size of `64kb` bad things happen: The while loop in `BlockManager::FindBlockPos` never terminates, and the node runs oom because memory for `m_blockfile_info` is allocated in each iteration of the loop. The same would happen if a naive user used `-fastprune` on anything other than regtest (so this can be tested by syncing on signet for example, the first block that crashes the node is at height 2232). Change the approach by raising the blockfile size to the size of the block, if that block otherwise wouldn't fit (idea by TheCharlatan). ACKs for top commit: ryanofsky: Code review ACK 8f14fc8. Added new assert, test, and comment since last review TheCharlatan: ACK 8f14fc8 pinheadmz: ACK 8f14fc8 Tree-SHA512: df2fea30613ef9d40ebbc2416eacb574f6d7d96847db5c33dda22a29a2c61a8db831aa9552734ea4477e097f253dbcb6dcb1395d43d2a090cc0588c9ce66eac3 * fix: adapt feature_fastprune.py for Dash (no SegWit witness support) Bitcoin's test uses a SegWit witness annex to create an oversized block. Dash has no SegWit, so instead create a 64 KiB+ tx via MiniWallet's target_weight with an OP_RETURN padding output (using -datacarriersize to allow the large data carrier). Mine it with generateblock to bypass mempool standardness rules. This exercises the same code path as Bitcoin's test: a block whose serialized size exceeds the 64 KiB fastprune blockfile limit, triggering the dynamic max_blockfile_size adjustment from bitcoin#27191. * fix: generate blocks to MiniWallet address in feature_fastprune test The test was generating blocks to self.nodes[0] (the node's default address) instead of wallet (MiniWallet's address). This meant MiniWallet had no spendable UTXOs, causing a StopIteration when create_self_transfer tried to find one. Pass wallet instead of self.nodes[0] to self.generate() so that MiniWallet.generate() sends coinbase rewards to its own address and tracks them as spendable UTXOs. * fix: remove absolute block count assert in feature_fastprune (Dash starts at 200) * ci: retrigger CI (flaky llmq signing test) --------- Co-authored-by: fanquake <fanquake@gmail.com> Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
…ock to measure durations (#11) * Merge bitcoin#27405: util: Use steady clock instead of system clock to measure durations fa83fb3 wallet: Use steady clock to calculate number of derive iterations (MarcoFalke) fa2c099 wallet: Use steady clock to measure scanning duration (MarcoFalke) fa97621 qt: Use steady clock to throttle GUI notifications (MarcoFalke) fa1d804 test: Use steady clock in index tests (MarcoFalke) fa454dc net: Use steady clock in InterruptibleRecv (MarcoFalke) Pull request description: `GetTimeMillis` has multiple issues: * It doesn't denote the underlying clock type * It isn't type-safe * It is used incorrectly in places that should use a steady clock Fix all issues here. ACKs for top commit: willcl-ark: ACK fa83fb3 martinus: Code review ACK bitcoin@fa83fb3, also ran all tests. All usages of the steady_clock are just for duration measurements, so the change to a different epoch is ok. Tree-SHA512: 5ec4fede8c7f97e2e08863c011856e8304f16ba30a68fdeb42f96a50a04961092cbe46ccf9ea6ac99ff5203c09f9e0924eb483eb38d7df0759addc85116c8a9f * fix: add MillisecondsDouble type alias for backport compatibility * fix: add missing util/time.h include to wallet.cpp The backport of bitcoin#27405 introduced usage of MillisecondsDouble and SteadyClock in wallet/wallet.cpp but didn't add the corresponding include for util/time.h, causing build failures across all CI platforms. --------- Co-authored-by: fanquake <fanquake@gmail.com> Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
090e3ac to
5cf8960
Compare
…ion marker to h) Adapt Dash-specific wallet code, CoinJoin descriptor detection, and functional tests to use the 'h' hardened derivation marker introduced by bitcoin#26076. Updates both descriptor and legacy wallet test paths.
5cf8960 to
6ef4468
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
Clean backport of 13 Bitcoin Core v0.26 PRs. All conflict resolutions are correct and Dash adaptations (no SegWit, CoinJoin, masternode subsystems) are properly handled throughout. No blocking issues. One convergent soft prerequisite noted (bitcoin#25781 for BlockManager::Options), correctly adapted around. Two minor pre-existing code observations noted.
Reviewed commit: 6ef4468
💬 2 nitpick(s)
| /** | ||
| * An options struct for `BlockManager`, more ergonomically referred to as | ||
| * `BlockManager::Options` due to the using-declaration in `BlockManager`. | ||
| */ | ||
| struct BlockManagerOpts { | ||
| const CChainParams& chainparams; | ||
| }; |
There was a problem hiding this comment.
💬 Nitpick: Soft prerequisite: bitcoin#25781 (BlockManager globals removal)
Upstream bitcoin#27570 builds on top of bitcoin#25781 which moved pruning globals into BlockManager::Options. Since bitcoin#25781 is not backported to Dash, this PR creates blockmanager_opts.h from scratch with only chainparams (no prune_target), and keeps the existing fPruneMode/nPruneTarget globals in blockstorage.h. This is correctly handled — the Dash commit adapts cleanly by initializing m_blockman{{chainparams}} in ChainstateManager's constructor. No semantic gap, just noting the divergence for tracking purposes.
source: ['claude', 'codex']
| mempoolmin_feerate = node.getmempoolinfo()["mempoolminfee"] | ||
| target_weight_each = 50000 | ||
| assert_greater_than(target_weight_each * 2, node.getmempoolinfo()["maxmempool"] - node.getmempoolinfo()["bytes"]) | ||
| parent_fee = (mempoolmin_feerate / 1000) * (target_weight_each // 4) - Decimal("0.00001") |
There was a problem hiding this comment.
💬 Nitpick: Vestigial // 4 weight-to-vbyte conversion from Bitcoin
parent_fee = (mempoolmin_feerate / 1000) * (target_weight_each // 4) - Decimal("0.00001") — the // 4 converts Bitcoin weight units to vbytes, but in Dash weight == serialized size (no SegWit discount). The test still passes because the assertion checks parent is below mempoolminfee (true, just by a wider margin than Bitcoin's version). Not blocking, but the edge case is tested less precisely.
💡 Suggested change
| parent_fee = (mempoolmin_feerate / 1000) * (target_weight_each // 4) - Decimal("0.00001") | |
| parent_fee = (mempoolmin_feerate / 1000) * target_weight_each - Decimal("0.00001") |
source: ['claude']
Batch 1 of Bitcoin Core v0.26 backports.
13 commits:
utxoupdatepsbtalso look for the tx in the txindex bitcoin/bitcoin#25939 — rpc: Inutxoupdatepsbtalso look for the tx in the txindexsendmanyrpc that usessubtractfeefromparameter bitcoin/bitcoin#26733 — test: Add test forsendmanyrpc that usessubtractfeefromparameterconverttopsbtcheck cleanups in rpc_psbt.py bitcoin/bitcoin#27325 — test: variousconverttopsbtcheck cleanups in rpc_psbt.pyAutomated backport via PastaClaw backport pipeline.
Each commit was individually cherry-picked, build-tested, reviewer-verified, and CI-validated on the fork before batching.