mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-24 22:58:46 +01:00
lnbits migrations work
This commit is contained in:
parent
7111639446
commit
c36f7a48aa
8 changed files with 358 additions and 158 deletions
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
emit-value
|
||||
v-model="formDialog.data.wallet"
|
||||
:options="g.user.walletOptions"
|
||||
label="Wallet *"
|
||||
label="Cashu wallet *"
|
||||
></q-select>
|
||||
<q-toggle
|
||||
v-model="toggleAdvanced"
|
||||
|
@ -156,7 +156,7 @@
|
|||
dense
|
||||
v-model.trim="formDialog.data.tickershort"
|
||||
label="Ticker shorthand"
|
||||
placeholder="CC"
|
||||
placeholder="sats"
|
||||
#
|
||||
></q-input>
|
||||
</div>
|
||||
|
@ -229,7 +229,7 @@
|
|||
{
|
||||
name: 'wallet',
|
||||
align: 'left',
|
||||
label: 'Wallet',
|
||||
label: 'Cashu wallet',
|
||||
field: 'wallet'
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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 %}
|
||||
<q-page-container>
|
||||
<q-page>
|
||||
|
@ -14,9 +14,8 @@ page_container %}
|
|||
rounded
|
||||
color="secondary"
|
||||
class="full-width"
|
||||
@click="showBuyTokensDialog"
|
||||
>Buy tokens
|
||||
<h5 class="text-caption q-ml-sm q-mb-none">(with sats)</h5>
|
||||
@click="showInvoicesDialog"
|
||||
>Create invoice
|
||||
</q-btn>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
@ -34,8 +33,7 @@ page_container %}
|
|||
rounded
|
||||
color="secondary"
|
||||
class="full-width"
|
||||
>Sell tokens
|
||||
<h5 class="text-caption q-ml-sm q-mb-none">(for sats)</h5>
|
||||
>Pay invoice
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,11 +113,11 @@ page_container %}
|
|||
<q-table
|
||||
dense
|
||||
flat
|
||||
:data="buyOrders"
|
||||
:columns="buysTable.columns"
|
||||
:pagination.sync="buysTable.pagination"
|
||||
no-data-label="No buys made yet"
|
||||
:filter="buysTable.filter"
|
||||
:data="invoicesCashu"
|
||||
:columns="invoicesTable.columns"
|
||||
:pagination.sync="invoicesTable.pagination"
|
||||
no-data-label="No invoices made yet"
|
||||
:filter="invoicesTable.filter"
|
||||
>
|
||||
{% raw %}
|
||||
<template v-slot:body="props">
|
||||
|
@ -137,7 +135,7 @@ page_container %}
|
|||
size="lg"
|
||||
color="secondary"
|
||||
class="q-mr-md cursor-pointer"
|
||||
@click="recheckBuyOrder(props.row.hash)"
|
||||
@click="recheckInvoice(props.row.hash)"
|
||||
>
|
||||
Recheck
|
||||
</q-badge>
|
||||
|
@ -182,11 +180,15 @@ page_container %}
|
|||
active-class="px-0"
|
||||
indicator-color="transparent"
|
||||
>
|
||||
<q-tab icon="arrow_right" label="Buy" @click="showBuyTokensDialog">
|
||||
<q-tab
|
||||
icon="arrow_right"
|
||||
label="Create Invoice"
|
||||
@click="showInvoicesDialog"
|
||||
>
|
||||
</q-tab>
|
||||
<q-tab icon="arrow_downward" label="Receive"></q-tab>
|
||||
<q-tab icon="arrow_upward" label="Send"></q-tab>
|
||||
<q-tab icon="arrow_right" label="Sell"> </q-tab>
|
||||
<q-tab icon="arrow_downward" label="Receive Tokens"></q-tab>
|
||||
<q-tab icon="arrow_upward" label="Send Token"></q-tab>
|
||||
<q-tab icon="arrow_right" label="Pay Invoice"> </q-tab>
|
||||
</q-tabs>
|
||||
|
||||
<q-dialog v-model="disclaimerDialog.show">
|
||||
|
@ -214,7 +216,7 @@ page_container %}
|
|||
|
||||
<q-dialog v-model="showInvoiceDetails" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<div v-if="!buyData.bolt11">
|
||||
<div v-if="!invoiceData.bolt11">
|
||||
<div class="row items-center no-wrap q-mb-sm">
|
||||
<div class="col-12">
|
||||
<span class="text-subtitle1"
|
||||
|
@ -225,7 +227,7 @@ page_container %}
|
|||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="buyData.amount"
|
||||
v-model.number="invoiceData.amount"
|
||||
label="Amount (sats) *"
|
||||
type="number"
|
||||
class="q-mb-lg"
|
||||
|
@ -234,15 +236,15 @@ page_container %}
|
|||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="buyData.memo"
|
||||
v-model.trim="invoiceData.memo"
|
||||
label="Memo"
|
||||
></q-input>
|
||||
</div>
|
||||
<div v-else class="text-center q-mb-lg">
|
||||
<a :href="'lightning:' + buyData.bolt11">
|
||||
<a :href="'lightning:' + invoiceData.bolt11">
|
||||
<q-responsive :ratio="1" class="q-mx-xl">
|
||||
<qrcode
|
||||
:value="buyData.bolt11"
|
||||
:value="invoiceData.bolt11"
|
||||
:options="{width: 340}"
|
||||
class="rounded-borders"
|
||||
>
|
||||
|
@ -252,8 +254,8 @@ page_container %}
|
|||
</div>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
v-if="buyData.bolt11"
|
||||
@click="copyText(buyData.bolt11)"
|
||||
v-if="invoiceData.bolt11"
|
||||
@click="copyText(invoiceData.bolt11)"
|
||||
outline
|
||||
color="grey"
|
||||
>Copy invoice</q-btn
|
||||
|
@ -449,8 +451,8 @@ page_container %}
|
|||
mintId: '',
|
||||
mintName: '',
|
||||
keys: '',
|
||||
buyOrders: [],
|
||||
buyData: {
|
||||
invoicesCashu: [],
|
||||
invoiceData: {
|
||||
amount: 0,
|
||||
memo: '',
|
||||
bolt11: '',
|
||||
|
@ -507,7 +509,7 @@ page_container %}
|
|||
}
|
||||
},
|
||||
payments: [],
|
||||
buysTable: {
|
||||
invoicesTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'status',
|
||||
|
@ -873,17 +875,17 @@ page_container %}
|
|||
},
|
||||
|
||||
/////////////////////////////////// WALLET ///////////////////////////////////
|
||||
showBuyTokensDialog: async function () {
|
||||
console.log('##### showBuyTokensDialog')
|
||||
this.buyData.amount = 0
|
||||
this.buyData.bolt11 = ''
|
||||
this.buyData.hash = ''
|
||||
this.buyData.memo = ''
|
||||
showInvoicesDialog: async function () {
|
||||
console.log('##### showInvoicesDialog')
|
||||
this.invoiceData.amount = 0
|
||||
this.invoiceData.bolt11 = ''
|
||||
this.invoiceData.hash = ''
|
||||
this.invoiceData.memo = ''
|
||||
this.showInvoiceDetails = true
|
||||
},
|
||||
|
||||
showInvoiceDialog: function (data) {
|
||||
this.buyData = _.clone(data)
|
||||
this.invoiceData = _.clone(data)
|
||||
this.showInvoiceDetails = true
|
||||
},
|
||||
|
||||
|
@ -911,20 +913,20 @@ page_container %}
|
|||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'GET',
|
||||
`/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.buyData.amount}`
|
||||
`/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.invoiceData.amount}`
|
||||
)
|
||||
console.log('### data', data)
|
||||
|
||||
this.buyData.bolt11 = data.pr
|
||||
this.buyData.hash = data.hash
|
||||
this.buyOrders.push({
|
||||
..._.clone(this.buyData),
|
||||
this.invoiceData.bolt11 = data.pr
|
||||
this.invoiceData.hash = data.hash
|
||||
this.invoicesCashu.push({
|
||||
..._.clone(this.invoiceData),
|
||||
date: currentDateStr(),
|
||||
status: 'pending'
|
||||
})
|
||||
this.storeBuyOrders()
|
||||
const amounts = splitAmount(this.buyData.amount)
|
||||
await this.requestTokens(amounts, this.buyData.hash)
|
||||
this.storeinvoicesCashu()
|
||||
const amounts = splitAmount(this.invoiceData.amount)
|
||||
await this.requestTokens(amounts, this.invoiceData.hash)
|
||||
this.tab = 'orders'
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
@ -933,12 +935,12 @@ page_container %}
|
|||
},
|
||||
|
||||
checkXXXXXX: async function () {
|
||||
for (const tokenBuy of this.buyOrders) {
|
||||
if (tokenBuy.status === 'pending') {
|
||||
for (const invoice of this.invoicesCashu) {
|
||||
if (invoice.status === 'pending') {
|
||||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'POST',
|
||||
`/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${tokenBuy.hash}`,
|
||||
`/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${invoice.hash}`,
|
||||
'',
|
||||
{
|
||||
blinded_messages: []
|
||||
|
@ -953,10 +955,10 @@ page_container %}
|
|||
}
|
||||
},
|
||||
|
||||
recheckBuyOrder: async function (hash) {
|
||||
console.log('### recheckBuyOrder.hash', hash)
|
||||
recheckInvoice: async function (hash) {
|
||||
console.log('### recheckInvoice.hash', hash)
|
||||
const tokens = this.tokens.find(bt => bt.hash === hash)
|
||||
console.log('### recheckBuyOrder.tokens', tokens)
|
||||
console.log('### recheckInvoice.tokens', tokens)
|
||||
if (!tokens) {
|
||||
console.error('####### no token for hash', hash)
|
||||
return
|
||||
|
@ -970,9 +972,9 @@ page_container %}
|
|||
tokens.status = 'paid'
|
||||
this.storeTokens()
|
||||
|
||||
const buyOrder = this.buyOrders.find(bo => bo.hash === hash)
|
||||
buyOrder.status = 'paid'
|
||||
this.storeBuyOrders()
|
||||
const invoice = this.invoicesCashu.find(bo => bo.hash === hash)
|
||||
invoice.status = 'paid'
|
||||
this.storeinvoicesCashu()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1261,8 +1263,11 @@ page_container %}
|
|||
localStorage.setItem('cashu.keys', JSON.stringify(data))
|
||||
},
|
||||
|
||||
storeBuyOrders: function () {
|
||||
localStorage.setItem('cashu.buyOrders', JSON.stringify(this.buyOrders))
|
||||
storeinvoicesCashu: function () {
|
||||
localStorage.setItem(
|
||||
'cashu.invoicesCashu',
|
||||
JSON.stringify(this.invoicesCashu)
|
||||
)
|
||||
},
|
||||
storeTokens: function () {
|
||||
localStorage.setItem(
|
||||
|
@ -1285,8 +1290,8 @@ page_container %}
|
|||
!params.get('tsh') &&
|
||||
!this.$q.localStorage.getItem('cashu.tickershort')
|
||||
) {
|
||||
this.$q.localStorage.set('cashu.tickershort', 'CE')
|
||||
this.tickershort = 'CE'
|
||||
this.$q.localStorage.set('cashu.tickershort', 'sats')
|
||||
this.tickershort = 'sats'
|
||||
} else if (params.get('tsh')) {
|
||||
this.$q.localStorage.set('cashu.tickershort', params.get('tsh'))
|
||||
this.tickershort = params.get('tsh')
|
||||
|
@ -1322,11 +1327,11 @@ page_container %}
|
|||
this.keys = JSON.parse(keysJson)
|
||||
}
|
||||
|
||||
this.buyOrders = JSON.parse(
|
||||
localStorage.getItem('cashu.buyOrders') || '[]'
|
||||
this.invoicesCashu = JSON.parse(
|
||||
localStorage.getItem('cashu.invoicesCashu') || '[]'
|
||||
)
|
||||
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
|
||||
console.log('### buyOrders', this.buyOrders)
|
||||
console.log('### invoicesCashu', this.invoicesCashu)
|
||||
console.table('### tokens', this.tokens)
|
||||
console.log('#### this.mintId', this.mintId)
|
||||
console.log('#### this.mintName', this.mintName)
|
||||
|
|
|
@ -28,7 +28,8 @@ from .crud import (
|
|||
store_promise,
|
||||
update_lightning_invoice,
|
||||
)
|
||||
from .ledger import mint, request_mint
|
||||
|
||||
# from .ledger import mint, request_mint
|
||||
from .mint import generate_promises, get_pubkeys, melt, split
|
||||
from .models import (
|
||||
Cashu,
|
||||
|
@ -207,15 +208,15 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
|
|||
########################################
|
||||
|
||||
|
||||
@cashu_ext.get("/api/v1/cashu/{cashu_id}/keys", status_code=HTTPStatus.OK)
|
||||
async def keys(cashu_id: str = Query(False)):
|
||||
"""Get the public keys of the mint"""
|
||||
mint = await get_cashu(cashu_id)
|
||||
if mint is None:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
|
||||
)
|
||||
return get_pubkeys(mint.prvkey)
|
||||
# @cashu_ext.get("/api/v1/cashu/{cashu_id}/keys", status_code=HTTPStatus.OK)
|
||||
# async def keys(cashu_id: str = Query(False)):
|
||||
# """Get the public keys of the mint"""
|
||||
# mint = await get_cashu(cashu_id)
|
||||
# if mint is None:
|
||||
# raise HTTPException(
|
||||
# status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
|
||||
# )
|
||||
# return get_pubkeys(mint.prvkey)
|
||||
|
||||
|
||||
@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
|
||||
|
@ -355,3 +356,129 @@ async def split_proofs(payload: SplitRequest, cashu_id: str = Query(None)):
|
|||
resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
|
||||
print("### resp", json.dumps(resp, default=vars))
|
||||
return resp
|
||||
|
||||
|
||||
##################################################################
|
||||
##################################################################
|
||||
# CASHU LIB
|
||||
##################################################################
|
||||
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from fastapi import APIRouter
|
||||
from secp256k1 import PublicKey
|
||||
|
||||
from cashu.core.base import (
|
||||
BlindedSignature,
|
||||
CheckFeesRequest,
|
||||
CheckFeesResponse,
|
||||
CheckRequest,
|
||||
GetMeltResponse,
|
||||
GetMintResponse,
|
||||
MeltRequest,
|
||||
MintRequest,
|
||||
PostSplitResponse,
|
||||
SplitRequest,
|
||||
)
|
||||
from cashu.core.errors import CashuError
|
||||
|
||||
from . import db, ledger
|
||||
|
||||
|
||||
@cashu_ext.get("/keys")
|
||||
async def keys() -> dict[int, str]:
|
||||
"""Get the public keys of the mint"""
|
||||
return ledger.get_keyset()
|
||||
|
||||
|
||||
@cashu_ext.get("/keysets")
|
||||
async def keysets() -> dict[str, list[str]]:
|
||||
"""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.
|
||||
This endpoint can be used for a Lightning invoice UX flow.
|
||||
|
||||
Call `POST /mint` after paying the invoice.
|
||||
"""
|
||||
payment_request, payment_hash = await ledger.request_mint(amount)
|
||||
print(f"Lightning invoice: {payment_request}")
|
||||
resp = GetMintResponse(pr=payment_request, hash=payment_hash)
|
||||
return resp
|
||||
|
||||
|
||||
@cashu_ext.post("/mint")
|
||||
async def mint(
|
||||
payloads: MintRequest,
|
||||
payment_hash: Union[str, None] = None,
|
||||
) -> Union[List[BlindedSignature], CashuError]:
|
||||
"""
|
||||
Requests the minting of tokens belonging to a paid payment request.
|
||||
|
||||
Call this endpoint after `GET /mint`.
|
||||
"""
|
||||
amounts = []
|
||||
B_s = []
|
||||
for payload in payloads.blinded_messages:
|
||||
amounts.append(payload.amount)
|
||||
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
|
||||
try:
|
||||
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
|
||||
return promises
|
||||
except Exception as exc:
|
||||
return CashuError(error=str(exc))
|
||||
|
||||
|
||||
@cashu_ext.post("/melt")
|
||||
async def melt(payload: MeltRequest) -> GetMeltResponse:
|
||||
"""
|
||||
Requests tokens to be destroyed and sent out via Lightning.
|
||||
"""
|
||||
ok, preimage = await ledger.melt(payload.proofs, payload.invoice)
|
||||
resp = GetMeltResponse(paid=ok, preimage=preimage)
|
||||
return resp
|
||||
|
||||
|
||||
@cashu_ext.post("/check")
|
||||
async def check_spendable(payload: CheckRequest) -> Dict[int, bool]:
|
||||
"""Check whether a secret has been spent already or not."""
|
||||
return await ledger.check_spendable(payload.proofs)
|
||||
|
||||
|
||||
@cashu_ext.post("/checkfees")
|
||||
async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
|
||||
"""
|
||||
Responds with the fees necessary to pay a Lightning invoice.
|
||||
Used by wallets for figuring out the fees they need to supply.
|
||||
This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu).
|
||||
"""
|
||||
fees_msat = await ledger.check_fees(payload.pr)
|
||||
return CheckFeesResponse(fee=fees_msat / 1000)
|
||||
|
||||
|
||||
@cashu_ext.post("/split")
|
||||
async def split(
|
||||
payload: SplitRequest,
|
||||
) -> Union[CashuError, PostSplitResponse]:
|
||||
"""
|
||||
Requetst a set of tokens with amount "total" to be split into two
|
||||
newly minted sets with amount "split" and "total-split".
|
||||
"""
|
||||
proofs = payload.proofs
|
||||
amount = payload.amount
|
||||
outputs = payload.outputs.blinded_messages if payload.outputs else None
|
||||
# backwards compatibility with clients < v0.2.2
|
||||
assert outputs, Exception("no outputs provided.")
|
||||
try:
|
||||
split_return = await ledger.split(proofs, amount, outputs)
|
||||
except Exception as exc:
|
||||
return CashuError(error=str(exc))
|
||||
if not split_return:
|
||||
return CashuError(error="there was an error with the split")
|
||||
frst_promises, scnd_promises = split_return
|
||||
resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
|
||||
return resp
|
||||
|
|
Loading…
Add table
Reference in a new issue