Skip to content

fix: prevent negative balances during transaction confirmation#2094

Merged
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:fix/confirm-balance-recheck
Apr 5, 2026
Merged

fix: prevent negative balances during transaction confirmation#2094
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:fix/confirm-balance-recheck

Conversation

@createkr
Copy link
Copy Markdown
Contributor

@createkr createkr commented Apr 5, 2026

Fix: re-check sender balance in confirm_transaction() + add CHECK constraint

What

Prevents negative-balance minting in TransactionPool.confirm_transaction() by adding a balance re-check before deduction, plus a schema-level CHECK(balance_urtc >= 0) constraint with automatic migration.

Why

confirm_transaction() previously deducted from the sender's balance without verifying sufficient funds at confirmation time. If the balance dropped between submit_transaction() and confirm_transaction() (e.g., another tx confirmed in the same block, direct DB mutation), the sender's balance would go negative — creating funds from nothing.

Changes

node/rustchain_tx_handler.py

  1. confirm_transaction() — Added balance re-check before the sender deduction:

    • SELECT balance_urtc FROM balances WHERE wallet = ? before the UPDATE
    • Returns False with error log if sender_balance < tx.amount_urtc
    • No state mutation occurs on rejection (history insert is also skipped)
  2. _ensure_schema() — Added automatic migration to add CHECK(balance_urtc >= 0):

    • Detects existing constraint via sqlite_master inspection
    • Recreates table with constraint (SQLite doesn't support ALTER TABLE ADD CHECK)
    • Only migrates rows with balance_urtc >= 0 (preserves non-negative data)
    • Gracefully handles migration failures with a warning log

node/tests/test_confirm_balance_recheck.py (new)

5 focused tests:

  • test_confirm_rejects_when_balance_insufficient — balance drained before confirm → rejected
  • test_confirm_succeeds_when_balance_sufficient — normal path still works
  • test_confirm_rejects_exact_balance — confirming for exact balance → succeeds (balance → 0)
  • test_confirm_rejects_unknown_sender — no balance row → rejected
  • test_check_constraint_prevents_negative_balance — direct negative INSERT blocked by DB

Test Results

tests/test_confirm_balance_recheck.py  — 5 passed
tests/test_tx_negative_amount_rejected.py — 2 passed
tests/test_ledger.py — 4 passed
Total: 11/11 passed

Risk

  • Low — The balance re-check is a pure addition that only adds a rejection path. Normal confirmations are unaffected.
  • The schema migration is idempotent and wrapped in try/except. Existing databases with negative balances will have those rows excluded from migration (logged as warning).

@github-actions github-actions bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related tests Test suite changes labels Apr 5, 2026
@createkr
Copy link
Copy Markdown
Contributor Author

createkr commented Apr 5, 2026

For bounty payout, please use RTC wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35.

@github-actions github-actions bot added the size/XL PR: 500+ lines label Apr 5, 2026
@Scottcjn Scottcjn merged commit d3e2550 into Scottcjn:main Apr 5, 2026
7 of 9 checks passed
@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 5, 2026

Merged. 50 RTC. CHECK(balance_urtc >= 0) constraint + confirm-time balance recheck prevents negative-balance minting between submit and confirm.

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/XL PR: 500+ lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants