Skip to content

backport: partial Merge bitcoin/bitcoin#26326: net: reduce LOCK(cs_main) scope in GETBLOCKTXN#7350

Open
thepastaclaw wants to merge 1 commit into
dashpay:developfrom
thepastaclaw:fix/getblocktxn-recent-disk-read
Open

backport: partial Merge bitcoin/bitcoin#26326: net: reduce LOCK(cs_main) scope in GETBLOCKTXN#7350
thepastaclaw wants to merge 1 commit into
dashpay:developfrom
thepastaclaw:fix/getblocktxn-recent-disk-read

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jun 9, 2026

Copy link
Copy Markdown

backport: partial Merge bitcoin#26326

Upstream backport

This PR is a partial backport of bitcoin#26326:

Issue being fixed or feature implemented

Recent GETBLOCKTXN handling selected and read the requested block from disk while holding cs_main. A peer can request recent block transactions repeatedly, so the disk I/O should not extend the validation/global chain lock hold time.

What was done?

  • Capture the recent block's disk position while holding cs_main.
  • Release cs_main before reading the block from disk.
  • Verify the read block hash still matches the requested block hash before sending BLOCKTXN.
  • Treat a missing, pruned, or mismatched read defensively by logging and returning instead of asserting.

How Has This Been Tested?

Tested on macOS arm64.

  • git diff --check upstream/develop...HEAD
  • ./configure --without-gui --disable-bench --disable-fuzz-binary with the macOS arm64 depends config.site
  • make -j8 src/dashd
  • Pre-PR code review gate: ship

After converting the PR history to backport metadata, the source tree stayed unchanged from the previously reviewed/rebased head.

Breaking Changes

None.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 792ed148-9daf-46b0-8df6-d2fca9970dc2

📥 Commits

Reviewing files that changed from the base of the PR and between c6ccbc6 and 037d040.

📒 Files selected for processing (1)
  • src/net_processing.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/net_processing.cpp

Walkthrough

This PR adds a compile-time check that MAX_BLOCKTXN_DEPTH does not exceed MIN_BLOCKS_TO_KEEP. It also updates GETBLOCKTXN handling to read deep blocks from the stored disk position, verify the returned hash matches the requested block hash, and only then send BLOCKTXN.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • UdjinM6
  • PastaPastaPasta
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: a partial backport reducing LOCK(cs_main) scope in GETBLOCKTXN.
Description check ✅ Passed The description is directly about the same backport and accurately summarizes the implemented changes and testing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@thepastaclaw thepastaclaw force-pushed the fix/getblocktxn-recent-disk-read branch from 6440dea to 97af883 Compare June 9, 2026 02:38
@thepastaclaw

thepastaclaw commented Jun 9, 2026

Copy link
Copy Markdown
Author

✅ Review complete (commit 037d040)

@thepastaclaw thepastaclaw marked this pull request as ready for review June 9, 2026 04:52

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Targeted change narrows the cs_main hold window in GETBLOCKTXN by capturing the block file position under the lock and performing the disk read outside it, with a hash check that fails closed on stale/pruned/mismatched reads. Both agents concur the change is correct; only a minor log-granularity nitpick remains.

💬 1 nitpick(s)

Comment thread src/net_processing.cpp
Comment on lines +4523 to 4525
if (!hash || *hash != req.blockhash) {
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we could not read\n", pfrom.GetId());
return;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Nitpick: Log message conflates I/O failure with hash mismatch

After dropping cs_main, the failure path emits the same "a block we could not read" line for two distinct conditions: ReadBlockFromDisk returning nullopt (open/deserialize/PoW failure) and the read succeeding with *hash != req.blockhash (a stale FlatFilePos / disk-state inconsistency after the lock was released). Distinguishing these helps operators tell transient I/O or pruning issues from a position-vs-hash race. Diagnostic quality only; not a defect.

source: ['claude']

@PastaPastaPasta

Copy link
Copy Markdown
Member

Is this not code we inherited from bitcoin? it is rare we want to accept a "fix" in their code.

@thepastaclaw

Copy link
Copy Markdown
Author

Good question. I checked this against upstream Bitcoin: the change is actually bringing this path back in line with the current inherited Bitcoin pattern, not inventing a Dash-only behavior.

Bitcoin master already copies the recent block position under cs_main, releases the lock, then reads the block from disk outside cs_main and serves BLOCKTXN. This PR does the same for Dash, with the Dash-local ReadBlockFromDisk API/hash check shape.

So I agree with the general rule — we should be cautious about “fixing” inherited Bitcoin code — but in this case the safer path is to follow the upstream Bitcoin fix rather than keep Dash on the older wider-lock version.

@PastaPastaPasta

Copy link
Copy Markdown
Member

@thepastaclaw

Bitcoin master already copies the recent block position under cs_main, releases the lock, then reads the block from disk outside cs_main and serves BLOCKTXN. This PR does the same for Dash, with the Dash-local ReadBlockFromDisk API/hash check shape.

Hmm; it seems then, that there may be a backport that we should backport instead of doing this adhoc?

@thepastaclaw

Copy link
Copy Markdown
Author

Yep — I traced it more precisely and there is an exact upstream Bitcoin commit for this:

That commit adds the same MAX_BLOCKTXN_DEPTH <= MIN_BLOCKS_TO_KEEP assertion and moves the GETBLOCKTXN disk read out from under cs_main. This PR is effectively that backport adapted to Dash's current ReadBlockFromDisk shape / block hash validation.

So I agree with your process concern: this should be treated as a Bitcoin backport, not as an ad-hoc Dash-only fix. If you prefer, I can close this PR and re-run it through the normal backport flow; otherwise I can update the PR metadata/commit message to explicitly cite the upstream commit.

@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@thepastaclaw thepastaclaw force-pushed the fix/getblocktxn-recent-disk-read branch from 97af883 to c6ccbc6 Compare June 27, 2026 12:04
@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@thepastaclaw

Copy link
Copy Markdown
Author

Rebased this onto current develop and pushed c6ccbc6. GitHub now reports the PR branch as mergeable and predict_conflicts passed. The remaining check_merge failure is the repository-level master/develop fast-forward check, not a conflict in this PR branch.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Cumulative review at c6ccbc6: the GETBLOCKTXN lock-scope reduction is correct — FlatFilePos is captured under cs_main, disk I/O runs lock-free, and the post-read hash check fails closed on stale/mismatched reads. No new in-scope defects in the latest delta. Carrying forward the prior nitpick about the log message conflating read failure with hash mismatch (already tracked in an unresolved inline thread).

💬 1 nitpick(s)

Note: GitHub does not allow me to submit APPROVE on my own PR, so this is posted as a COMMENT while preserving the verified non-blocking findings.

Comment thread src/net_processing.cpp
Comment on lines +4495 to 4497
if (!hash || *hash != req.blockhash) {
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we could not read\n", pfrom.GetId());
return;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Nitpick: Log line conflates disk read failure with hash mismatch

After cs_main is released, two operationally distinct failures share one log line: ReadBlockFromDisk returning std::nullopt (OpenBlockFile failure, deserialization error, PoW header check failure — typically pruning or corruption) and a successful read whose *hash != req.blockhash (the FlatFilePos captured under cs_main no longer points at the requested block, e.g. a race with reorg/prune/reindex in the very window this PR introduces). Splitting these into two messages would make the new lock-scope reduction easier to diagnose if it surfaces in the wild. Purely diagnostic quality; behavior is correct. Existing unresolved thread: #7350 (comment).

source: ['claude', 'codex']

@PastaPastaPasta

Copy link
Copy Markdown
Member

Claw; please update this to actually be a backport, if it should so be instead of an adhoc change that doesn't accomplish / get shown in history as a backport

…OCKTXN

613a45c net: reduce LOCK(cs_main) scope in GETBLOCKTXN (Andrew Toth)

Pull request description:

  Inspired by bitcoin#11913 and bitcoin#26308.

  `cs_main` doesn't need to be locked while reading blocks. This removes the locks in `net_processing`.

Dash notes:

  This is a partial backport of bitcoin#26326. It includes only the GETBLOCKTXN commit, adapted to Dash's current ReadBlockFromDisk API and post-read hash validation. The sibling upstream ProcessGetBlockData commit is not included in this PR.
@thepastaclaw thepastaclaw force-pushed the fix/getblocktxn-recent-disk-read branch from c6ccbc6 to 037d040 Compare June 27, 2026 14:03
@thepastaclaw thepastaclaw changed the title fix: reduce GETBLOCKTXN disk read lock scope backport: partial Merge bitcoin/bitcoin#26326: net: reduce LOCK(cs_main) scope in GETBLOCKTXN Jun 27, 2026
@thepastaclaw

Copy link
Copy Markdown
Author

Updated. I amended the PR head so the commit history now records this as a partial backport of bitcoin#26326, specifically bitcoin/bitcoin@613a45c (net: reduce LOCK(cs_main) scope in GETBLOCKTXN). I also updated the PR title/body to make the backport source explicit. The tree is unchanged from the previously reviewed/rebased head; this push is metadata/history only.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Cumulative review at 037d040. Source tree is unchanged from c6ccbc6; the push only rewrote commit metadata into proper Dash partial-backport form. The GETBLOCKTXN lock-scope reduction is correct: FlatFilePos captured under cs_main, disk read runs lock-free, post-read hash check fails closed on mismatch/missing/pruned. Only carried-forward finding is a low-confidence log-diagnostic nitpick about line 4496 conflating two distinct failure modes.

💬 1 nitpick(s)

Carried-forward findings already raised (1)

These findings were not re-posted as new inline comments because an existing review thread already covers them.

  • [NITPICK] (deduped existing open thread) src/net_processing.cpp:4495-4497: Log message conflates I/O failure with hash mismatch — Two operationally distinct failures share one log message: ReadBlockFromDisk returning std::nullopt (OpenBlockFile failure, deserialization error, PoW header check failure — typically pruning/corruption) and a successful read whose *hash != req.blockhash (the FlatFilePos captured under cs_m...

Note: GitHub does not allow me to approve my own PR, so this is posted as a COMMENT while preserving the verified nonblocking finding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants