more endpoints working

This commit is contained in:
callebtc
2022-10-14 00:06:02 +02:00
committed by dni ⚡
parent b41bd694a1
commit 5b1ee554df
2 changed files with 154 additions and 38 deletions

View File

@@ -13,8 +13,7 @@ class Cashu(BaseModel):
fraction: bool = Query(None) fraction: bool = Query(None)
maxsats: int = Query(0) maxsats: int = Query(0)
coins: int = Query(0) coins: int = Query(0)
prvkey: str = Query(None) keyset_id: str = Query(None)
pubkey: str = Query(None)
@classmethod @classmethod
def from_row(cls, row: Row): def from_row(cls, row: Row):

View File

@@ -1,6 +1,7 @@
import json import json
from http import HTTPStatus from http import HTTPStatus
from typing import Union from typing import Union
import math
import httpx import httpx
from fastapi import Query from fastapi import Query
@@ -9,14 +10,21 @@ from lnurl import decode as decode_lnurl
from loguru import logger from loguru import logger
from secp256k1 import PublicKey from secp256k1 import PublicKey
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from lnbits import bolt11
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.core.services import check_transaction_status, create_invoice from lnbits.core.services import (
check_transaction_status,
create_invoice,
fee_reserve,
pay_invoice,
)
from lnbits.core.views.api import api_payment from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.wallets.base import PaymentStatus from lnbits.wallets.base import PaymentStatus
from lnbits.helpers import urlsafe_short_hash from lnbits.helpers import urlsafe_short_hash
from lnbits.core.crud import check_internal
from . import cashu_ext from . import cashu_ext
from .core.base import CashuError, PostSplitResponse, SplitRequest from .core.base import CashuError, PostSplitResponse, SplitRequest
from .crud import ( from .crud import (
@@ -47,10 +55,10 @@ from .models import (
############### IMPORT CALLE ############### IMPORT CALLE
from typing import Dict, List, Union from typing import Dict, List, Union
from fastapi import APIRouter
from secp256k1 import PublicKey from secp256k1 import PublicKey
from cashu.core.base import ( from cashu.core.base import (
Proof,
BlindedSignature, BlindedSignature,
CheckFeesRequest, CheckFeesRequest,
CheckFeesResponse, CheckFeesResponse,
@@ -63,7 +71,9 @@ from cashu.core.base import (
SplitRequest, SplitRequest,
) )
from cashu.core.errors import CashuError from cashu.core.errors import CashuError
from . import db, ledger
LIGHTNING = False
######################################## ########################################
############### LNBITS MINTS ########### ############### LNBITS MINTS ###########
@@ -99,63 +109,170 @@ async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key
####################################### #######################################
from . import db, ledger @cashu_ext.get("/{cashu_id}/keys", status_code=HTTPStatus.OK)
async def keys(cashu_id: str = Query(None)) -> dict[int, str]:
"""Get the public keys of the mint"""
@cashu_ext.get("{cashu_id}/keys", status_code=HTTPStatus.OK) cashu: Union[Cashu, None] = await get_cashu(cashu_id)
async def keys(cashu_id: str = None) -> dict[int, str]:
cashu = await get_cashu(cashu_id)
if not cashu: if not cashu:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
) )
"""Get the public keys of the mint""" return ledger.get_keyset(keyset_id=cashu.keyset_id)
return ledger.get_keyset()
@cashu_ext.get("/keysets") @cashu_ext.get("/{cashu_id}/mint")
async def keysets() -> dict[str, list[str]]: async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintResponse:
"""Get all active keysets of the mint"""
return {"keysets": await ledger.keysets.get_ids()}
@cashu_ext.get("/mint")
async def request_mint(amount: int = 0) -> GetMintResponse:
""" """
Request minting of new tokens. The mint responds with a Lightning invoice. Request minting of new tokens. The mint responds with a Lightning invoice.
This endpoint can be used for a Lightning invoice UX flow. This endpoint can be used for a Lightning invoice UX flow.
Call `POST /mint` after paying the invoice. Call `POST /mint` after paying the invoice.
""" """
payment_request, payment_hash = await ledger.request_mint(amount) cashu: Union[Cashu, None] = await get_cashu(cashu_id)
if not cashu:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
)
# create an invoice that the wallet needs to pay
try:
payment_hash, payment_request = await create_invoice(
wallet_id=cashu.wallet,
amount=amount,
memo=f"{cashu.name}",
extra={"tag": "cashu"},
)
invoice = Invoice(
amount=amount, pr=payment_request, hash=payment_hash, issued=False
)
# await store_lightning_invoice(cashu_id, invoice)
await ledger.crud.store_lightning_invoice(invoice=invoice, db=ledger.db)
except Exception as e:
logger.error(e)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
print(f"Lightning invoice: {payment_request}") print(f"Lightning invoice: {payment_request}")
resp = GetMintResponse(pr=payment_request, hash=payment_hash) resp = GetMintResponse(pr=payment_request, hash=payment_hash)
# return {"pr": payment_request, "hash": payment_hash}
return resp return resp
@cashu_ext.post("/mint") @cashu_ext.post("/{cashu_id}/mint")
async def mint( async def mint_coins(
payloads: MintRequest, data: MintRequest,
payment_hash: Union[str, None] = None, cashu_id: str = Query(None),
) -> Union[List[BlindedSignature], CashuError]: payment_hash: str = Query(None),
):
""" """
Requests the minting of tokens belonging to a paid payment request. Requests the minting of tokens belonging to a paid payment request.
Call this endpoint after `GET /mint`. Call this endpoint after `GET /mint`.
""" """
amounts = [] cashu: Union[Cashu, None] = await get_cashu(cashu_id)
B_s = [] if cashu is None:
for payload in payloads.blinded_messages: raise HTTPException(
amounts.append(payload.amount) status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True)) )
if LIGHTNING:
invoice: Invoice = await ledger.crud.get_lightning_invoice(
db=ledger.db, hash=payment_hash
)
if invoice is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Mint does not have this invoice.",
)
if invoice.issued == True:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED,
detail="Tokens already issued for this invoice.",
)
total_requested = sum([bm.amount for bm in data.blinded_messages])
if total_requested > invoice.amount:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED,
detail=f"Requested amount too high: {total_requested}. Invoice amount: {invoice.amount}",
)
status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash)
# todo: revert to: status.paid != True:
if status.paid != True:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
try: try:
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash) await ledger.crud.update_lightning_invoice(
db=ledger.db, hash=payment_hash, issued=True
)
keyset = ledger.keysets.keysets[cashu.keyset_id]
promises = await ledger._generate_promises(
B_s=data.blinded_messages, keyset=keyset
)
return promises return promises
except Exception as exc: except Exception as e:
return CashuError(error=str(exc)) logger.error(e)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
@cashu_ext.post("/{cashu_id}/melt")
async def melt_coins(payload: MeltRequest, cashu_id: str = Query(None)):
"""Invalidates proofs and pays a Lightning invoice."""
cashu: Union[None, Cashu] = await get_cashu(cashu_id)
if cashu is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
)
proofs = payload.proofs
invoice = payload.invoice
# async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
# """Invalidates proofs and pays a Lightning invoice."""
# !!!!!!! MAKE SURE THAT PROOFS ARE ONLY FROM THIS CASHU KEYSET ID
# THIS IS NECESSARY BECAUSE THE CASHU BACKEND WILL ACCEPT ANY VALID
# TOKENS
assert all([p.id == cashu.keyset_id for p in proofs]), HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Proofs include tokens from other mint.",
)
assert all([ledger._verify_proof(p) for p in proofs]), HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Could not verify proofs.",
)
total_provided = sum([p["amount"] for p in proofs])
invoice_obj = bolt11.decode(invoice)
amount = math.ceil(invoice_obj.amount_msat / 1000)
internal_checking_id = await check_internal(invoice_obj.payment_hash)
if not internal_checking_id:
fees_msat = fee_reserve(invoice_obj.amount_msat)
else:
fees_msat = 0
assert total_provided >= amount + fees_msat / 1000, Exception(
f"Provided proofs ({total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)."
)
await pay_invoice(
wallet_id=cashu.wallet,
payment_request=invoice,
description=f"pay cashu invoice",
extra={"tag": "cashu", "cahsu_name": cashu.name},
)
status: PaymentStatus = await check_transaction_status(
cashu.wallet, invoice_obj.payment_hash
)
if status.paid == True:
await ledger._invalidate_proofs(proofs)
return status.paid, status.preimage
return False, ""
@cashu_ext.post("/melt") @cashu_ext.post("/melt")