Skip to content

[UTXO-BUG] Preserve caller-owned UTXO DB connection#5572

Merged
Scottcjn merged 1 commit into
Scottcjn:mainfrom
iamdinhthuan:fix-utxo-conn-ownership
May 18, 2026
Merged

[UTXO-BUG] Preserve caller-owned UTXO DB connection#5572
Scottcjn merged 1 commit into
Scottcjn:mainfrom
iamdinhthuan:fix-utxo-conn-ownership

Conversation

@iamdinhthuan
Copy link
Copy Markdown
Contributor

Summary

  • stop UtxoDB.apply_transaction() from closing caller-supplied SQLite connections on the rejected mining_reward guard path
  • add a regression proving a caller can still inspect and roll back its external transaction after a rejected mint attempt

Why

apply_transaction(..., conn=caller_conn) accepts a caller-owned connection. On the unauthorized mining_reward validation path, the current code closes that external connection before returning False. That turns a normal validation failure into sqlite3.ProgrammingError: Cannot operate on a closed database for callers that need to inspect or roll back their own transaction.

Validation

# RED before production fix
python3 -m pytest node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_preserves_external_conn node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_rejected_before_opening_db -q

Observed before fix: test_user_supplied_mining_reward_preserves_external_conn failed because the caller-owned connection was closed.

python3 -m pytest node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_preserves_external_conn node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_rejected_before_opening_db node/test_utxo_db.py -q
python3 -m py_compile node/utxo_db.py node/test_utxo_db.py
git diff --check

Observed locally: 72 passed, py_compile passed, and git diff --check was clean.

Duplicate / safety checks

Before opening this PR I searched public issues and PRs for caller-owned connection apply_transaction mining_reward, external conn close apply_transaction mining_reward, and preserve external connection apply_transaction. I did not find an exact duplicate. Open PR #4638 is about unmineable mempool outputs, not this external-connection ownership path.

Bounty route: Scottcjn/rustchain-bounties#2819.
Miner / payout id: iamdinhthuan.
No private keys, tokens, credentials, or account-internal data are included.

@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related labels May 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Welcome to RustChain! Thanks for your first pull request.

Before we review, please make sure:

  • Non-doc PRs have a BCOS-L1 or BCOS-L2 label
  • Doc-only PRs are exempt from BCOS tier labels when they only touch docs/**, *.md, or common image/PDF files
  • New code files include an SPDX license header
  • You've tested your changes against the live node

Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150)

A maintainer will review your PR soon. Thanks for contributing!

Copy link
Copy Markdown

@ZacharyZhang-NY ZacharyZhang-NY left a comment

Choose a reason for hiding this comment

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

Reviewed PR #5572 at commit 6123eb4. The change correctly preserves caller-owned SQLite connection ownership on the rejected mining_reward guard path while keeping owned connections unopened for unauthorized mint attempts. Validation passed: git diff --check for node/utxo_db.py and node/test_utxo_db.py; py_compile for both files; focused UTXO connection-ownership tests passed with 2 passed; full node/test_utxo_db.py passed with 72 passed. I did not find a blocking issue in this patch.

Copy link
Copy Markdown
Contributor

@LubuSeb LubuSeb left a comment

Choose a reason for hiding this comment

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

Disclosure: I am reviewing this under Scottcjn/rustchain-bounties#73 as @LubuSeb.

Approved on head 6123eb4722b36fecdcc0894219b4d47239d7c525.

The change is narrowly scoped and fixes the ownership bug without weakening the unauthorized mining_reward guard. apply_transaction() now rejects user-supplied mint attempts before opening an owned DB connection, while preserving a caller-supplied connection for callers that need to inspect or roll back their own transaction. The added regression covers the external-connection path, and the existing UTXO tests still pass.

Validation I ran:

git diff --check origin/main...HEAD -- node/utxo_db.py node/test_utxo_db.py
python -B -m py_compile node/utxo_db.py node/test_utxo_db.py
python -B -m pytest -q node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_preserves_external_conn node/test_utxo_db.py::TestUtxoDB::test_user_supplied_mining_reward_rejected_before_opening_db node/test_utxo_db.py --tb=short
# 72 passed

I did not find a blocking issue in this PR.

Copy link
Copy Markdown

@TJCurnutte TJCurnutte left a comment

Choose a reason for hiding this comment

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

Approved. I validated this head locally and the ownership boundary is now right.

Proof run at head 6123eb4722b36fecdcc0894219b4d47239d7c525:

git diff --check origin/main...HEAD -- node/test_utxo_db.py node/utxo_db.py
PYTHONPATH=node python3 -B -m py_compile node/utxo_db.py node/test_utxo_db.py
PYTHONPATH=node python3 -B -m pytest -q -o addopts='' --noconftest node/test_utxo_db.py

Result: 72 passed in 0.38s.

I also ran a direct caller-owned-connection probe against the rejected mining_reward path. apply_transaction() returned False, SELECT 1 on the same connection still worked, and conn.in_transaction stayed True, so the caller still owns cleanup/rollback after the rejection.

The two-line code change is scoped: the unauthorized minting guard still rejects user-supplied mining_reward, but it no longer closes a connection it did not create. The new regression test covers that exact failure mode.

@508704820
Copy link
Copy Markdown
Contributor

Security Review ✅ HIGH

Preserve caller-owned UTXO DB connection on rejected mining reward path. Previously, apply_transaction() would close the caller-supplied connection on rejection, preventing the caller from rolling back its external transaction. This could leave the database in an inconsistent state — a state corruption bug.

Reviewed by Xeophon - Solana: Lt9nERv6VHsojw15LpFeiaabuphAggzfLF9sM9UXRrZ

@weilixiong
Copy link
Copy Markdown
Contributor

Claiming and fixing - one-line fix: change "if conn:" to "if own:" on mining_reward guard path.

@weilixiong
Copy link
Copy Markdown
Contributor

Claiming and fixing - one-line fix: change "if conn:" to "if own:" on mining_reward guard path.

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

LGTM! Great work on this PR. 🚀

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@kekehanshujun kekehanshujun left a comment

Choose a reason for hiding this comment

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

I reviewed the UTXO connection ownership fix. The previous early-return path closed conn even when the caller supplied it, which breaks callers that are managing an outer transaction. Removing that close and adding a regression that keeps the externally-owned connection usable after a rejected unauthorized mining_reward addresses the issue directly.

I do not see a blocker in this focused patch.

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

@Scottcjn Scottcjn merged commit 9e219f5 into Scottcjn:main May 18, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related size/S PR: 11-50 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants