Skip to content

fix: prevent negative shadow balances in utxo dual-write#2126

Merged
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-utxo-dual-write-shadow-balance
Apr 6, 2026
Merged

fix: prevent negative shadow balances in utxo dual-write#2126
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-utxo-dual-write-shadow-balance

Conversation

@createkr
Copy link
Copy Markdown
Contributor

@createkr createkr commented Apr 6, 2026

Fix: UTXO dual-write shadow-balance guard

Summary

Add a balance re-check before the UTXO→account-model dual-write debit in utxo_endpoints.py. Prevents negative shadow balances when the account-model ledger diverges from UTXO balances.

Changes

node/utxo_endpoints.py

  • Added: Shadow-balance SELECT before the dual-write UPDATE debit
  • Added: Conditional skip when shadow_balance < amount_i64 — logs warning, skips dual-write, UTXO tx still succeeds (preserves "UTXO is primary" design)
  • Behavior: When the guard triggers, neither the debit, credit, nor ledger entries are written to the account model

node/tests/test_dual_write_shadow_balance.py (new)

  • 6 focused tests:
    • test_dual_write_skipped_when_shadow_balance_insufficient — core exploit path blocked
    • test_dual_write_skipped_when_sender_missing_from_shadow — missing sender row handled
    • test_dual_write_succeeds_when_shadow_sufficient — normal path unaffected
    • test_dual_write_exact_balance_goes_to_zero — boundary case (balance → 0)
    • test_no_ledger_entries_when_dual_write_skipped — no partial writes
    • test_utxo_succeeds_when_dual_write_disabled — dual_write=False unaffected

node/tests/test_dual_write_unit_mismatch.py (updated)

  • Updated 5 existing tests to seed shadow balances so dual-write can proceed under the new guard
  • Fixed test_dual_write_debit_matches_credit assertion (was expecting negative balance, which was the old buggy behavior)

Validation

30 passed in 0.42s
- tests/test_dual_write_unit_mismatch.py: 9 passed
- tests/test_dual_write_shadow_balance.py: 6 passed
- test_utxo_endpoints.py: 15 passed

Distinction from Prior Findings

Compatibility

  • Backward compatible: UTXO transactions succeed regardless of shadow balance state
  • Dual-write behavior: When shadow balance is insufficient, dual-write is silently skipped (same failure mode as any other dual-write exception)
  • No schema changes: Uses existing balances table, no migrations needed
  • No config changes: Works with existing UTXO_DUAL_WRITE flag

Risk Assessment

  • Low risk: The change is a single SELECT + conditional before existing UPDATEs
  • No broad refactors: Minimal surgical fix, ~15 lines of logic added
  • Preserves existing design: "UTXO is primary, account is shadow" — UTXO tx never fails due to shadow state

@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 6, 2026
@createkr
Copy link
Copy Markdown
Contributor Author

createkr commented Apr 6, 2026

For bounty payout, please use RTC wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35.

@github-actions github-actions bot added the size/L PR: 201-500 lines label Apr 6, 2026
@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 6, 2026

Reviewed. Adds shadow balance guard in utxo_endpoints.py dual-write path — checks sender has sufficient amount_i64 before debit, skips dual-write (with warning log) if insufficient rather than driving the balance negative. Prevents fund minting when account-model diverges from UTXO. 346-line test + updates to 6 existing tests to seed proper shadow balances. Also correctly reverts the Decimal conversion back to float (UTXO uses int() truncation consistently).

Payment: 85 RTC — UTXO negative shadow balance minting (Critical severity)

Merging. Thank you createkr.

@Scottcjn Scottcjn merged commit f79c316 into Scottcjn:main Apr 6, 2026
8 of 9 checks passed
mgrigajtis pushed a commit to mgrigajtis/Rustchain that referenced this pull request Apr 6, 2026
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/L PR: 201-500 lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants