Unique multi-use links can be over-withdrawn: usescsv and used are whole-row read-modify-writes with no serialisation across sub-links #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The
create_hash_checkmutex inapi_lnurl_callback(views_lnurl.py:135) is keyed onid_unique_hash or unique_hash. Foris_uniquelinks each sub-link gets its own key, so two claims on different sub-links of the same link run concurrently. Both then mutate link-wide fields from their own stale snapshot:remove_unique_withdraw_link(crud.py:98-105) recomputesusescsvin memory andupdate_withdraw_linkwrites the whole row;increment_withdraw_link(crud.py:108-111) doeslink.used = link.used + 1and writes the whole row again. Interleaving resurrects an already-spent sub-link inusescsvand under-countsused, so real payouts continue pastuses. These are the links bitSpire mints for cash-in payouts.Fix direction: funnel claim-time allowance mutation through one crud primitive that runs read -> check ->
UPDATE ... SET used = used + 1, usescsv = :rest WHERE id = :id AND used < usesinside a singledb.connect()block (the extension's asyncio DB lock serialises it on both SQLite and Postgres), treating 0 rows affected as spent. Sandbox PR #11 (reserve_withdraw_link_use,tests/test_allowance.pywith a 20-way concurrency test) implements this and can be ported as-is since the fork is still at the same commit.Found during reforge run #1 (sandbox withdraw#2).