Files
nutshell/cashu/mint/db/write.py
callebtc e846acf946 Mint: add websockets for quote updates (#413)
* add websockets for quote updates

* add test (not working)

* wip: emit events to everyone

* wip: emit events to everyone

* wip, lots of things broken but invoice callback works

* wip

* add wip files

* tests almost passing

* add task

* refactor nut constants

* startup fix

* works with old mints

* wip cli

* fix mypy

* remove automatic invoice test now with websockets

* remove comment

* better logging

* send back response

* add rate limiter to websocket

* add rate limiter to subscriptions

* refactor websocket ratelimit

* websocket tests

* subscription kinds

* doesnt start

* remove circular import

* update

* fix mypy

* move test file in test because it fails if it runs later... dunno why

* adjust websocket NUT-06 settings

* local import and small fix

* disable websockets in CLI if "no_check" is selected

* move subscription test to where it was

* check proof state with callback, add tests

* tests: run mint fixture per module instead of per session

* subscription command name fix

* test per session again

* update test race conditions

* fix tests

* clean up

* tmp

* fix db issues and remove cached secrets

* fix tests

* blindly try pipeline

* remove comments

* comments
2024-06-25 19:20:03 +02:00

98 lines
3.6 KiB
Python

import asyncio
from typing import List, Optional
from loguru import logger
from ...core.base import Proof, ProofState, SpentState
from ...core.db import Connection, Database, get_db_connection
from ...core.errors import (
TransactionError,
)
from ..crud import LedgerCrud
from ..events.events import LedgerEventManager
class DbWriteHelper:
db: Database
crud: LedgerCrud
events: LedgerEventManager
proofs_pending_lock: asyncio.Lock = (
asyncio.Lock()
) # holds locks for proofs_pending database
def __init__(
self, db: Database, crud: LedgerCrud, events: LedgerEventManager
) -> None:
self.db = db
self.crud = crud
self.events = events
async def _set_proofs_pending(
self, proofs: List[Proof], quote_id: Optional[str] = None
) -> None:
"""If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to
the list of pending proofs or removes them. Used as a mutex for proofs.
Args:
proofs (List[Proof]): Proofs to add to pending table.
quote_id (Optional[str]): Melt quote ID. If it is not set, we assume the pending tokens to be from a swap.
Raises:
Exception: At least one proof already in pending table.
"""
# first we check whether these proofs are pending already
async with self.proofs_pending_lock:
async with get_db_connection(self.db) as conn:
await self._validate_proofs_pending(proofs, conn)
try:
for p in proofs:
await self.crud.set_proof_pending(
proof=p, db=self.db, quote_id=quote_id, conn=conn
)
await self.events.submit(
ProofState(Y=p.Y, state=SpentState.pending)
)
except Exception as e:
logger.error(f"Failed to set proofs pending: {e}")
raise TransactionError("Failed to set proofs pending.")
async def _unset_proofs_pending(self, proofs: List[Proof], spent=True) -> None:
"""Deletes proofs from pending table.
Args:
proofs (List[Proof]): Proofs to delete.
spent (bool): Whether the proofs have been spent or not. Defaults to True.
This should be False if the proofs were NOT invalidated before calling this function.
It is used to emit the unspent state for the proofs (otherwise the spent state is emitted
by the _invalidate_proofs function when the proofs are spent).
"""
async with self.proofs_pending_lock:
async with get_db_connection(self.db) as conn:
for p in proofs:
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)
if not spent:
await self.events.submit(
ProofState(Y=p.Y, state=SpentState.unspent)
)
async def _validate_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
) -> None:
"""Checks if any of the provided proofs is in the pending proofs table.
Args:
proofs (List[Proof]): Proofs to check.
Raises:
Exception: At least one of the proofs is in the pending table.
"""
if not (
len(
await self.crud.get_proofs_pending(
Ys=[p.Y for p in proofs], db=self.db, conn=conn
)
)
== 0
):
raise TransactionError("proofs are pending.")