diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py index cb62ffcad..b36e94ef8 100644 --- a/lnbits/extensions/cashu/__init__.py +++ b/lnbits/extensions/cashu/__init__.py @@ -9,7 +9,26 @@ from lnbits.tasks import catch_everything_and_restart db = Database("ext_cashu") -cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"]) +import sys + +sys.path.append("/Users/cc/git/cashu") +from cashu.mint.ledger import Ledger +from .crud import LedgerCrud + +# db = Database("ext_cashu", LNBITS_DATA_FOLDER) + +ledger = Ledger( + db=db, + # seed=MINT_PRIVATE_KEY, + seed="asd", + derivation_path="0/0/0/1", + crud=LedgerCrud, +) + +cashu_ext: APIRouter = APIRouter(prefix="/api/v1/cashu", tags=["cashu"]) +# from cashu.mint.router import router as cashu_router + +# cashu_ext.include_router(router=cashu_router) cashu_static_files = [ { @@ -24,11 +43,12 @@ def cashu_renderer(): return template_renderer(["lnbits/extensions/cashu/templates"]) -from .tasks import wait_for_paid_invoices +from .tasks import wait_for_paid_invoices, startup_cashu_mint from .views import * # noqa from .views_api import * # noqa def cashu_start(): loop = asyncio.get_event_loop() + loop.create_task(catch_everything_and_restart(startup_cashu_mint)) loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json index 5262ea62a..e71e68895 100644 --- a/lnbits/extensions/cashu/config.json +++ b/lnbits/extensions/cashu/config.json @@ -1,7 +1,9 @@ { "name": "Cashu Ecash", - "short_description": "Ecash mints with LN peg in/out", + "short_description": "Ecash mint and wallet", "icon": "approval", "contributors": ["arcbtc", "calle", "vlad"], - "hidden": false + "hidden": false, + "migration_module": "cashu.mint.migrations", + "db_name": "cashu" } diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py index c8f3c72b0..48b90d70f 100644 --- a/lnbits/extensions/cashu/crud.py +++ b/lnbits/extensions/cashu/crud.py @@ -1,7 +1,8 @@ import os import random +import time from binascii import hexlify, unhexlify -from typing import List, Optional, Union +from typing import List, Optional, Union, Any from embit import bip32, bip39, ec, script from embit.networks import NETWORKS @@ -13,6 +14,49 @@ from . import db from .core.base import Invoice from .models import Cashu, Pegs, Promises, Proof +from cashu.core.base import MintKeyset +from lnbits.db import Database, Connection + + +class LedgerCrud: + """ + Database interface for Cashu mint. + + This class needs to be overloaded by any app that imports the Cashu mint. + """ + + async def get_keyset(*args, **kwags): + + return await get_keyset(*args, **kwags) + + async def get_lightning_invoice(*args, **kwags): + + return await get_lightning_invoice(*args, **kwags) + + async def get_proofs_used(*args, **kwags): + + return await get_proofs_used(*args, **kwags) + + async def invalidate_proof(*args, **kwags): + + return await invalidate_proof(*args, **kwags) + + async def store_keyset(*args, **kwags): + + return await store_keyset(*args, **kwags) + + async def store_lightning_invoice(*args, **kwags): + + return await store_lightning_invoice(*args, **kwags) + + async def store_promise(*args, **kwags): + + return await store_promise(*args, **kwags) + + async def update_lightning_invoice(*args, **kwags): + + return await update_lightning_invoice(*args, **kwags) + async def create_cashu(wallet_id: str, data: Cashu) -> Cashu: cashu_id = urlsafe_short_hash() @@ -120,9 +164,15 @@ async def get_promises(cashu_id) -> Optional[Cashu]: return Promises(**row) if row else None -async def get_proofs_used(cashu_id): - rows = await db.fetchall( - "SELECT secret from cashu.proofs_used WHERE cashu_id = ?", (cashu_id,) +async def get_proofs_used( + db: Database, + conn: Optional[Connection] = None, +): + + rows = await (conn or db).fetchall( + """ + SELECT secret from cashu.proofs_used + """ ) return [row[0] for row in rows] @@ -184,3 +234,62 @@ async def update_lightning_invoice(cashu_id: str, hash: str, issued: bool): hash, ), ) + + +############################## +######### KEYSETS ############ +############################## + + +async def store_keyset( + keyset: MintKeyset, + db: Database = None, + conn: Optional[Connection] = None, +): + + await (conn or db).execute( # type: ignore + """ + INSERT INTO cashu.keysets + (id, derivation_path, valid_from, valid_to, first_seen, active, version) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + ( + keyset.id, + keyset.derivation_path, + keyset.valid_from or db.timestamp_now, + keyset.valid_to or db.timestamp_now, + keyset.first_seen or db.timestamp_now, + True, + keyset.version, + ), + ) + + +async def get_keyset( + id: str = None, + derivation_path: str = "", + db: Database = None, + conn: Optional[Connection] = None, +): + clauses = [] + values: List[Any] = [] + clauses.append("active = ?") + values.append(True) + if id: + clauses.append("id = ?") + values.append(id) + if derivation_path: + clauses.append("derivation_path = ?") + values.append(derivation_path) + where = "" + if clauses: + where = f"WHERE {' AND '.join(clauses)}" + + rows = await (conn or db).fetchall( # type: ignore + f""" + SELECT * from cashu.keysets + {where} + """, + tuple(values), + ) + return [MintKeyset.from_row(row) for row in rows] diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py index 0cc6a7d4a..019ee84d8 100644 --- a/lnbits/extensions/cashu/migrations.py +++ b/lnbits/extensions/cashu/migrations.py @@ -1,79 +1 @@ -async def m001_initial(db): - """ - Initial cashu table. - """ - await db.execute( - """ - CREATE TABLE cashu.cashu ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - name TEXT NOT NULL, - tickershort TEXT DEFAULT 'sats', - fraction BOOL, - maxsats INT, - coins INT, - prvkey TEXT NOT NULL, - pubkey TEXT NOT NULL - ); - """ - ) - - """ - Initial cashus table. - """ - await db.execute( - """ - CREATE TABLE cashu.pegs ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - inout BOOL NOT NULL, - amount INT - ); - """ - ) - - """ - Initial cashus table. - """ - await db.execute( - """ - CREATE TABLE cashu.promises ( - id TEXT PRIMARY KEY, - amount INT, - B_b TEXT NOT NULL, - C_b TEXT NOT NULL, - cashu_id TEXT NOT NULL, - UNIQUE (B_b) - ); - """ - ) - - """ - Initial cashus table. - """ - await db.execute( - """ - CREATE TABLE cashu.proofs_used ( - id TEXT PRIMARY KEY, - amount INT, - C TEXT NOT NULL, - secret TEXT NOT NULL, - cashu_id TEXT NOT NULL - ); - """ - ) - - await db.execute( - """ - CREATE TABLE IF NOT EXISTS cashu.invoices ( - cashu_id TEXT NOT NULL, - amount INTEGER NOT NULL, - pr TEXT NOT NULL, - hash TEXT NOT NULL, - issued BOOL NOT NULL, - - UNIQUE (hash) - - ); - """ - ) +# this extension will use the migration_module module cashu.mint.migrations (see config.json) diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py index fe00a5918..9d2d7f15b 100644 --- a/lnbits/extensions/cashu/tasks.py +++ b/lnbits/extensions/cashu/tasks.py @@ -9,6 +9,21 @@ from lnbits.tasks import internal_invoice_queue, register_invoice_listener from .crud import get_cashu +import sys + +sys.path.append("/Users/cc/git/cashu") +# from cashu.mint import migrations +# from cashu.core.migrations import migrate_databases +from . import db, ledger + + +async def startup_cashu_mint(): + # await migrate_databases(db, migrations) + await ledger.load_used_proofs() + await ledger.init_keysets() + print(ledger.get_keyset()) + pass + async def wait_for_paid_invoices(): invoice_queue = asyncio.Queue() diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html index 4e5ba9119..dcb2b4a74 100644 --- a/lnbits/extensions/cashu/templates/cashu/index.html +++ b/lnbits/extensions/cashu/templates/cashu/index.html @@ -120,7 +120,7 @@ emit-value v-model="formDialog.data.wallet" :options="g.user.walletOptions" - label="Wallet *" + label="Cashu wallet *" > @@ -229,7 +229,7 @@ { name: 'wallet', align: 'left', - label: 'Wallet', + label: 'Cashu wallet', field: 'wallet' }, { diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index f64a19b72..4f12cd1c9 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -1,5 +1,5 @@ -{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Wallet -{% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block +{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu +wallet {% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block page_container %} @@ -14,9 +14,8 @@ page_container %} rounded color="secondary" class="full-width" - @click="showBuyTokensDialog" - >Buy tokens -
(with sats)
+ @click="showInvoicesDialog" + >Create invoice
@@ -34,8 +33,7 @@ page_container %} rounded color="secondary" class="full-width" - >Sell tokens -
(for sats)
+ >Pay invoice
@@ -115,11 +113,11 @@ page_container %} {% raw %}