Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions node/rustchain_p2p_gossip.py
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,10 @@ def receive_gossip():
if not _gossip_rate_check(remote_ip):
return jsonify({"error": "rate_limited", "limit": f"{GOSSIP_RATE_LIMIT}/{GOSSIP_RATE_WINDOW_S}s"}), 429

auth_error = _require_p2p_read_auth()
if auth_error:
return auth_error

if (
request.content_length is not None
and request.content_length > MAX_GOSSIP_REQUEST_BYTES
Expand Down
44 changes: 29 additions & 15 deletions node/utxo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -1535,9 +1535,16 @@ def mempool_get_block_candidates(self, max_count: int = 100) -> List[dict]:
conn.close()

def mempool_clear_expired(self) -> int:
"""Remove expired transactions from mempool. Returns count removed."""
"""Remove expired transactions from mempool. Returns count removed.

Uses BEGIN IMMEDIATE to ensure the SELECT-then-DELETE sequence is
atomic. Without it, a concurrent mempool_add() or apply_transaction()
can interleave between the SELECT and the DELETEs, causing mempool
state corruption / double-spend (B2, issue #8176).
"""
conn = self._conn()
try:
conn.execute("BEGIN IMMEDIATE")
now = int(time.time())
try:
expired = conn.execute(
Expand All @@ -1546,22 +1553,29 @@ def mempool_clear_expired(self) -> int:
).fetchall()
except sqlite3.OperationalError as exc:
if "no such table" in str(exc).lower():
conn.execute("ROLLBACK")
return 0
conn.execute("ROLLBACK")
raise
else:
count = 0
for row in expired:
conn.execute(
"DELETE FROM utxo_mempool_inputs WHERE tx_id = ?",
(row['tx_id'],),
)
conn.execute(
"DELETE FROM utxo_mempool WHERE tx_id = ?",
(row['tx_id'],),
)
count += 1
conn.commit()
return count
count = 0
for row in expired:
conn.execute(
"DELETE FROM utxo_mempool_inputs WHERE tx_id = ?",
(row['tx_id'],),
)
conn.execute(
"DELETE FROM utxo_mempool WHERE tx_id = ?",
(row['tx_id'],),
)
count += 1
conn.commit()
return count
except Exception:
try:
conn.execute("ROLLBACK")
except Exception:
pass
raise
finally:
conn.close()

Expand Down