TryBugHunt

The Dangling Ledger

Mediumpython

Two-Phase Fund Transfer with Swallowed Transaction Rollback

0:00
Step 1: Test Traffic & Expose Flaw
"""
Domain models for the wallet service.
"""

class AccountSuspendedError(Exception):
    pass

class InsufficientFundsError(Exception):
    pass

class Account:
    def __init__(self, account_id, balance=0.0, is_suspended=False):
        self.account_id = account_id
        self.balance = float(balance)
        self.is_suspended = is_suspended

    def debit(self, amount):
        if self.is_suspended:
            raise AccountSuspendedError(f"Account {self.account_id} is suspended")
        if self.balance < amount:
            raise InsufficientFundsError(f"Insufficient funds in account {self.account_id}")
        self.balance -= amount

    def credit(self, amount):
        if self.is_suspended:
            raise AccountSuspendedError(f"Account {self.account_id} is suspended")
        self.balance += amount
Step 1: Test Traffic & Expose FlawClient-side WASM

Run a scenario below to trace fund transfers. Prove that when a transfer to a suspended user fails, the sender's money is still debited.

Choose Traffic Scenario to TestClick to simulate
Architectural Guide & Interview Notes

Deep Dive: The Dangling Ledger

An engineering analysis of why this AI-generated Pull Request passed isolated unit tests, how multi-tenant traffic exposes the architectural flaw, and how interviewers score your response.

The Root Cause Anti-Pattern

In transactional financial systems, context managers (like with db.transaction():) only trigger rollback when an unhandled exception escapes. Swallowing domain exceptions (such as AccountSuspendedError) inside the transaction block without invoking session.rollback() or compensating the sender causes partial debit persistence, evaporating customer balances into thin air.

The Interviewer's Scoring Rubric

Candidates are assessed on whether they jump to trial-and-error editing or follow a hypothesis-driven approach. A "Strong Hire" candidate restates the data flow between modules, explains why single-client unit tests passed, crafts an adversarial probe showing cross-tenant leakage, and implements a surgical 1-line patch without breaking existing contracts.

Candidate Strategy for the Code Comprehension Round

  1. 1Map Module ContractsTrace how the gateway invokes the underlying service. Note what data is passed by reference vs. by value.
  2. 2Break It With An Adversarial ProbeSimulate concurrent or sequential traffic across multiple identities to force shared state mutation.
  3. 3Deliver a Surgical PatchMake the minimal required diff to ensure proper per-client state isolation without refactoring unnecessary files.