Say what bets and withdrawals actually exclude (B-70)
The flowchart's WITHDRAW node (E1) stated that a withdrawal cannot happen together with a bet in progress. The code only serializes the two *builds* through the per-user lock: a withdrawal is accepted while a bet is still unconfirmed, as long as confirmed, unspent UTXOs cover it. CLAUDE.md makes every node of the diagrams binding, so one of the two had to move, and it is the diagram. The hazard the node was reaching for is the two transactions picking the same UTXO, and that is already excluded twice: app/tx/locks.py keeps the builds from overlapping, and select_utxos skips anything already marked spent_txid. What the node forbade on top of that is spending untouched, confirmed money — so implementing it as written would freeze a user's whole balance for a block after every bet and protect nothing. E1 now describes the real rule, and CLAUDE.md's per-user-lock paragraph states it is the only exclusion between the two. Regenerated the A4/A3 PDFs (gitignored, so not in this commit). The regression test is behavioural, not a wording check: it funds a user with two confirmed UTXOs, bets (taking the larger), and asserts the withdrawal goes through on the other one with the bet still unconfirmed and neither transaction spending the other's input. A second test keeps the diagram from drifting back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ The user communicates in Italian in chat — reply to them in Italian. Everythin
|
||||
|
||||
## Project status
|
||||
|
||||
All 10 stages of the original build order are code-complete and unit-tested — 349 tests, all under `tests/unit/` (`tests/integration/` is an empty package). Beyond them: Docker + Caddy deployment, admin dashboard (`/admin`), static test UI (`/`), pending-inclusive balance display, an SSE push channel layered over the original polling, self-service password change + admin password reset, and the reconciliation/corroboration machinery below.
|
||||
All 10 stages of the original build order are code-complete and unit-tested — 351 tests, all under `tests/unit/` (`tests/integration/` is an empty package). Beyond them: Docker + Caddy deployment, admin dashboard (`/admin`), static test UI (`/`), pending-inclusive balance display, an SSE push channel layered over the original polling, self-service password change + admin password reset, and the reconciliation/corroboration machinery below.
|
||||
|
||||
Verified on mainnet with real money: registration + address derivation, deposit crediting (1-conf), a real 10 PLM bet (broadcast → confirmed → change credited back), and one full round cycle (close → draw on a real block hash → 70/30 payout with sat math checked against the broadcast tx → confirmation → close → next round auto-opened). **Withdrawal and the RBF bump path have never been exercised against a live broadcast** — unit-tested only.
|
||||
|
||||
@@ -33,7 +33,7 @@ PYTHONPATH=. python scripts/decrypt_master_key.py # ops recovery: decrypt+pr
|
||||
PYTHONPATH=. python scripts/encrypt_master_key.py # ops bootstrap: import an externally-generated xprv (--overwrite to replace)
|
||||
PYTHONPATH=. python scripts/electrum_smoke_test.py # manual check: connect, handshake, subscribe to headers, print the tip
|
||||
|
||||
python -m pytest # all 349 tests
|
||||
python -m pytest # all 351 tests
|
||||
python -m pytest tests/unit/test_hd.py # one file
|
||||
python -m pytest tests/unit/test_hd.py::test_derivation_is_deterministic # one test
|
||||
```
|
||||
@@ -166,7 +166,7 @@ Diagrams: [platform-overview.mmd](flowchart/platform-overview.mmd), [round-lifec
|
||||
|
||||
**WITHDRAW** — the only way out to an external address: PSBT user-address → external + change back to the user, fee deducted from the withdrawn amount, same RBF pattern. A full-balance withdrawal moves `balance - DUST_LIMIT_SATS` so the change output (and with it the ability to fee-bump) always exists — `Withdrawal.amount_requested_sats` vs `amount_sent_sats` is what records the difference (B-62).
|
||||
|
||||
PLAY and WITHDRAW share a **per-user lock** (`tx/locks.py`): a bet-build and a withdrawal-build can never be in flight at once, since both spend the same UTXO set.
|
||||
PLAY and WITHDRAW share a **per-user lock** (`tx/locks.py`): a bet-build and a withdrawal-build can never be in flight at once, since both spend the same UTXO set. That is the *only* mutual exclusion between them (B-70): a withdrawal is accepted while a bet is still unconfirmed, as long as confirmed, unspent UTXOs cover it — the lock plus `select_utxos` skipping anything already marked `spent_txid` is what prevents the two from picking the same input, so freezing the rest of the balance for a block on top of that would restrict the user without protecting anything.
|
||||
|
||||
**Three separate on-chain confirmations sit between the timer hitting zero and the payout landing** — a common point of confusion:
|
||||
1. **Last bet's confirmation** — the round doesn't even flip to `"closing"` until every broadcast bet has 1 conf (`_tick`'s `pending_count` check). May already have happened before the deadline.
|
||||
|
||||
Reference in New Issue
Block a user