Skip to content

Commit 0c40bbb

Browse files
a1denvalu3callebtc
andauthored
Balance Views Grouped By Keyset (cashubtc#652)
* balance view per keyset + relative changes. Still lacking: settings changes * import unit * fix 0 balance * settings --------- Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
1 parent 23a706d commit 0c40bbb

File tree

4 files changed

+66
-9
lines changed

4 files changed

+66
-9
lines changed

cashu/mint/crud.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ async def update_keyset(
119119
@abstractmethod
120120
async def get_balance(
121121
self,
122+
keyset: MintKeyset,
122123
db: Database,
123124
conn: Optional[Connection] = None,
124125
) -> int: ...
@@ -668,15 +669,22 @@ async def store_keyset(
668669

669670
async def get_balance(
670671
self,
672+
keyset: MintKeyset,
671673
db: Database,
672674
conn: Optional[Connection] = None,
673675
) -> int:
674676
row = await (conn or db).fetchone(
675677
f"""
676-
SELECT * from {db.table_with_schema('balance')}
677-
"""
678+
SELECT balance FROM {db.table_with_schema('balance')}
679+
WHERE keyset = :keyset
680+
""",
681+
{
682+
"keyset": keyset.id,
683+
},
678684
)
679-
assert row, "Balance not found"
685+
686+
if row is None:
687+
return 0
680688

681689
# sqlalchemy index of first element
682690
key = next(iter(row))

cashu/mint/ledger.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,9 @@ def get_keyset(self, keyset_id: Optional[str] = None) -> Dict[int, str]:
322322
raise KeysetError("no public keys for this keyset")
323323
return {a: p.serialize().hex() for a, p in keyset.public_keys.items()}
324324

325-
async def get_balance(self) -> int:
325+
async def get_balance(self, keyset: MintKeyset) -> int:
326326
"""Returns the balance of the mint."""
327-
return await self.crud.get_balance(db=self.db)
327+
return await self.crud.get_balance(keyset=keyset, db=self.db)
328328

329329
# ------- ECASH -------
330330

@@ -451,8 +451,13 @@ async def mint_quote(self, quote_request: PostMintQuoteRequest) -> MintQuote:
451451
):
452452
raise NotAllowedError("Backend does not support descriptions.")
453453

454-
if settings.mint_max_balance:
455-
balance = await self.get_balance()
454+
# MINT_MAX_BALANCE refers to sat (for now)
455+
if settings.mint_max_balance and unit == Unit.sat:
456+
# get next active keyset for unit
457+
active_keyset: MintKeyset = next(
458+
filter(lambda k: k.active and k.unit == unit, self.keysets.values())
459+
)
460+
balance = await self.get_balance(active_keyset)
456461
if balance + quote_request.amount > settings.mint_max_balance:
457462
raise NotAllowedError("Mint has reached maximum balance.")
458463

cashu/mint/migrations.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,3 +868,43 @@ async def m025_add_amounts_to_keysets(db: Database):
868868
await conn.execute(
869869
f"UPDATE {db.table_with_schema('keysets')} SET amounts = '[]'"
870870
)
871+
872+
873+
async def m026_keyset_specific_balance_views(db: Database):
874+
async with db.connect() as conn:
875+
await drop_balance_views(db, conn)
876+
await conn.execute(
877+
f"""
878+
CREATE VIEW {db.table_with_schema('balance_issued')} AS
879+
SELECT id AS keyset, COALESCE(s, 0) AS balance FROM (
880+
SELECT id, SUM(amount) AS s
881+
FROM {db.table_with_schema('promises')}
882+
WHERE amount > 0
883+
GROUP BY id
884+
);
885+
"""
886+
)
887+
await conn.execute(
888+
f"""
889+
CREATE VIEW {db.table_with_schema('balance_redeemed')} AS
890+
SELECT id AS keyset, COALESCE(s, 0) AS balance FROM (
891+
SELECT id, SUM(amount) AS s
892+
FROM {db.table_with_schema('proofs_used')}
893+
WHERE amount > 0
894+
GROUP BY id
895+
);
896+
"""
897+
)
898+
await conn.execute(
899+
f"""
900+
CREATE VIEW {db.table_with_schema('balance')} AS
901+
SELECT keyset, s_issued - s_used AS balance FROM (
902+
SELECT bi.keyset AS keyset,
903+
bi.balance AS s_issued,
904+
COALESCE(bu.balance, 0) AS s_used
905+
FROM {db.table_with_schema('balance_issued')} bi
906+
LEFT OUTER JOIN {db.table_with_schema('balance_redeemed')} bu
907+
ON bi.keyset = bu.keyset
908+
);
909+
"""
910+
)

tests/test_mint.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from cashu.core.base import BlindedMessage, Proof
5+
from cashu.core.base import BlindedMessage, MintKeyset, Proof, Unit
66
from cashu.core.crypto.b_dhke import step1_alice
77
from cashu.core.helpers import calculate_number_of_blank_outputs
88
from cashu.core.models import PostMintQuoteRequest
@@ -218,7 +218,11 @@ async def test_generate_change_promises_returns_empty_if_no_outputs(ledger: Ledg
218218

219219
@pytest.mark.asyncio
220220
async def test_get_balance(ledger: Ledger):
221-
balance = await ledger.get_balance()
221+
unit = Unit["sat"]
222+
active_keyset: MintKeyset = next(
223+
filter(lambda k: k.active and k.unit == unit, ledger.keysets.values())
224+
)
225+
balance = await ledger.get_balance(active_keyset)
222226
assert balance == 0
223227

224228

0 commit comments

Comments
 (0)