Merge pull request #1103 from lnbits/cashu_rewrite_wallet
Cashu rewrite wallet
This commit is contained in:
commit
9fdbaf92f6
16 changed files with 1941 additions and 703 deletions
|
|
@ -97,3 +97,8 @@ ECLAIR_PASS=eclairpw
|
||||||
# Enter /api in LightningTipBot to get your key
|
# Enter /api in LightningTipBot to get your key
|
||||||
LNTIPS_API_KEY=LNTIPS_ADMIN_KEY
|
LNTIPS_API_KEY=LNTIPS_ADMIN_KEY
|
||||||
LNTIPS_API_ENDPOINT=https://ln.tips
|
LNTIPS_API_ENDPOINT=https://ln.tips
|
||||||
|
|
||||||
|
# Cashu Mint
|
||||||
|
# Use a long-enough random (!) private key.
|
||||||
|
# Once set, you cannot change this key as for now.
|
||||||
|
CASHU_PRIVATE_KEY="SuperSecretPrivateKey"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from environs import Env # type: ignore
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
|
@ -20,10 +21,12 @@ cashu_static_files = [
|
||||||
]
|
]
|
||||||
from cashu.mint.ledger import Ledger
|
from cashu.mint.ledger import Ledger
|
||||||
|
|
||||||
|
env = Env()
|
||||||
|
env.read_env()
|
||||||
|
|
||||||
ledger = Ledger(
|
ledger = Ledger(
|
||||||
db=db,
|
db=db,
|
||||||
# seed=MINT_PRIVATE_KEY,
|
seed=env.str("CASHU_PRIVATE_KEY", default="SuperSecretPrivateKey"),
|
||||||
seed="asd",
|
|
||||||
derivation_path="0/0/0/1",
|
derivation_path="0/0/0/1",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "Cashu Ecash",
|
"name": "Cashu",
|
||||||
"short_description": "Ecash mint and wallet",
|
"short_description": "Ecash mint and wallet",
|
||||||
"icon": "approval",
|
"icon": "account_balance",
|
||||||
"contributors": ["arcbtc", "calle", "vlad"],
|
"contributors": ["calle", "vlad", "arcbtc"],
|
||||||
"hidden": false
|
"hidden": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "Cashu Ecash",
|
"name": "Cashu",
|
||||||
"short_description": "Ecash mints with LN peg in/out",
|
"short_description": "Ecash mints with LN peg in/out",
|
||||||
"icon": "approval",
|
"icon": "account_balance",
|
||||||
"contributors": ["arcbtc", "calle"],
|
"contributors": ["calle", "vlad", "arcbtc"],
|
||||||
"hidden": true
|
"hidden": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ async def m001_initial(db):
|
||||||
fraction BOOL,
|
fraction BOOL,
|
||||||
maxsats INT,
|
maxsats INT,
|
||||||
coins INT,
|
coins INT,
|
||||||
keyset_id TEXT NOT NULL
|
keyset_id TEXT NOT NULL,
|
||||||
|
issued_sat INT
|
||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,21 @@ async function hashToCurve(secretMessage) {
|
||||||
return point
|
return point
|
||||||
}
|
}
|
||||||
|
|
||||||
async function step1Bob(secretMessage) {
|
async function step1Alice(secretMessage) {
|
||||||
secretMessage = nobleSecp256k1.utils.bytesToHex(secretMessage)
|
// todo: document & validate `secretMessage` format
|
||||||
secretMessage = new TextEncoder().encode(secretMessage);
|
secretMessage = uint8ToBase64.encode(secretMessage)
|
||||||
|
secretMessage = new TextEncoder().encode(secretMessage)
|
||||||
const Y = await hashToCurve(secretMessage)
|
const Y = await hashToCurve(secretMessage)
|
||||||
const randomBlindingFactor = bytesToNumber(
|
const rpk = nobleSecp256k1.utils.randomPrivateKey()
|
||||||
nobleSecp256k1.utils.randomPrivateKey()
|
const r = bytesToNumber(rpk)
|
||||||
)
|
const P = nobleSecp256k1.Point.fromPrivateKey(r)
|
||||||
const P = nobleSecp256k1.Point.fromPrivateKey(randomBlindingFactor)
|
|
||||||
const B_ = Y.add(P)
|
const B_ = Y.add(P)
|
||||||
return {B_: B_.toHex(true), randomBlindingFactor}
|
return {B_: B_.toHex(true), r: nobleSecp256k1.utils.bytesToHex(rpk)}
|
||||||
}
|
}
|
||||||
|
|
||||||
function step3Bob(C_, r, A) {
|
function step3Alice(C_, r, A) {
|
||||||
const rInt = BigInt(r)
|
// const rInt = BigInt(r)
|
||||||
|
const rInt = bytesToNumber(r)
|
||||||
const C = C_.subtract(A.multiply(rInt))
|
const C = C_.subtract(A.multiply(rInt))
|
||||||
return C
|
return C
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ async def startup_cashu_mint():
|
||||||
await migrate_databases(db, migrations)
|
await migrate_databases(db, migrations)
|
||||||
await ledger.load_used_proofs()
|
await ledger.load_used_proofs()
|
||||||
await ledger.init_keysets()
|
await ledger.init_keysets()
|
||||||
print(ledger.get_keyset())
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
:content-inset-level="0.5"
|
:content-inset-level="0.5"
|
||||||
>
|
>
|
||||||
<q-btn flat label="Swagger API" type="a" href="../docs#/cashu"></q-btn>
|
<q-btn flat label="Swagger API" type="a" href="../docs#/cashu"></q-btn>
|
||||||
<q-expansion-item group="api" dense expand-separator label="List TPoS">
|
<!-- <q-expansion-item group="api" dense expand-separator label="List TPoS">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<code><span class="text-blue">GET</span> /cashu/api/v1/cashus</code>
|
<code><span class="text-blue">GET</span> /cashu/api/v1/mints</code>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
<code>[<cashu_object>, ...]</code>
|
<code>[<cashu_object>, ...]</code>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
||||||
<code
|
<code
|
||||||
>curl -X GET {{ request.base_url }}cashu/api/v1/cashus -H "X-Api-Key:
|
>curl -X GET {{ request.base_url }}cashu/api/v1/mints -H "X-Api-Key:
|
||||||
<invoice_key>"
|
<invoice_key>"
|
||||||
</code>
|
</code>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<q-expansion-item group="api" dense expand-separator label="Create a TPoS">
|
<q-expansion-item group="api" dense expand-separator label="Create a TPoS">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<code><span class="text-green">POST</span> /cashu/api/v1/cashus</code>
|
<code><span class="text-green">POST</span> /cashu/api/v1/mints</code>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
>
|
>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
||||||
<code
|
<code
|
||||||
>curl -X POST {{ request.base_url }}cashu/api/v1/cashus -d '{"name":
|
>curl -X POST {{ request.base_url }}cashu/api/v1/mints -d '{"name":
|
||||||
<string>, "currency": <string>}' -H "Content-type:
|
<string>, "currency": <string>}' -H "Content-type:
|
||||||
application/json" -H "X-Api-Key: <admin_key>"
|
application/json" -H "X-Api-Key: <admin_key>"
|
||||||
</code>
|
</code>
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<code
|
<code
|
||||||
><span class="text-pink">DELETE</span>
|
><span class="text-pink">DELETE</span>
|
||||||
/cashu/api/v1/cashus/<cashu_id></code
|
/cashu/api/v1/mints/<cashu_id></code
|
||||||
>
|
>
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
<code>{"X-Api-Key": <admin_key>}</code><br />
|
||||||
|
|
@ -71,10 +71,10 @@
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
||||||
<code
|
<code
|
||||||
>curl -X DELETE {{ request.base_url
|
>curl -X DELETE {{ request.base_url
|
||||||
}}cashu/api/v1/cashus/<cashu_id> -H "X-Api-Key:
|
}}cashu/api/v1/mints/<cashu_id> -H "X-Api-Key:
|
||||||
<admin_key>"
|
<admin_key>"
|
||||||
</code>
|
</code>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item> -->
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
<q-expansion-item group="extras" icon="info" label="About">
|
<q-expansion-item group="extras" icon="info" label="About">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<p>
|
<p>Create Cashu ecash mints and wallets.</p>
|
||||||
Make Ecash mints with peg in/out to a wallet, that can create and manage
|
|
||||||
ecash.
|
|
||||||
</p>
|
|
||||||
<small
|
<small
|
||||||
>Created by
|
>Created by
|
||||||
<a href="https://github.com/arcbtc" target="_blank">arcbtc</a>,
|
<a href="https://github.com/arcbtc" target="_blank">arcbtc</a>,
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,12 @@
|
||||||
unelevated
|
unelevated
|
||||||
dense
|
dense
|
||||||
size="xs"
|
size="xs"
|
||||||
icon="launch"
|
icon="account_balance_wallet"
|
||||||
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||||
type="a"
|
type="a"
|
||||||
:href="'wallet/?tsh=' + (props.row.tickershort || '') + '&mint_id=' + props.row.id + '&mint_name=' + props.row.name"
|
:href="'wallet/?' + 'mint_id=' + props.row.id"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
><q-tooltip>Shareable wallet page</q-tooltip></q-btn
|
><q-tooltip>Shareable wallet</q-tooltip></q-btn
|
||||||
>
|
>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
@ -218,18 +218,18 @@
|
||||||
toggleAdvanced: false,
|
toggleAdvanced: false,
|
||||||
cashusTable: {
|
cashusTable: {
|
||||||
columns: [
|
columns: [
|
||||||
{name: 'id', align: 'left', label: 'ID', field: 'id'},
|
{name: 'id', align: 'left', label: 'Mint ID', field: 'id'},
|
||||||
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
||||||
{
|
{
|
||||||
name: 'tickershort',
|
name: 'tickershort',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'tickershort',
|
label: 'Ticker',
|
||||||
field: 'tickershort'
|
field: 'tickershort'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'wallet',
|
name: 'wallet',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'Cashu wallet',
|
label: 'Mint wallet',
|
||||||
field: 'wallet'
|
field: 'wallet'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -271,7 +271,7 @@
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request(
|
.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/cashu/api/v1/cashus?all_wallets=true',
|
'/cashu/api/v1/mints?all_wallets=true',
|
||||||
this.g.user.wallets[0].inkey
|
this.g.user.wallets[0].inkey
|
||||||
)
|
)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
|
@ -294,7 +294,7 @@
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request(
|
.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/cashu/api/v1/cashus',
|
'/cashu/api/v1/mints',
|
||||||
_.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet})
|
_.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet})
|
||||||
.inkey,
|
.inkey,
|
||||||
data
|
data
|
||||||
|
|
@ -314,13 +314,13 @@
|
||||||
|
|
||||||
LNbits.utils
|
LNbits.utils
|
||||||
.confirmDialog(
|
.confirmDialog(
|
||||||
'Are you sure you want to delete this Mint? It will suck for users.'
|
"Are you sure you want to delete this Mint? This mint's users will not be able to redeem their tokens!"
|
||||||
)
|
)
|
||||||
.onOk(function () {
|
.onOk(function () {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request(
|
.request(
|
||||||
'DELETE',
|
'DELETE',
|
||||||
'/cashu/api/v1/cashus/' + cashuId,
|
'/cashu/api/v1/mints/' + cashuId,
|
||||||
_.findWhere(self.g.user.wallets, {id: cashu.wallet}).adminkey
|
_.findWhere(self.g.user.wallets, {id: cashu.wallet}).adminkey
|
||||||
)
|
)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "public.html" %} {% block page %}
|
{% extends "public.html" %} {% block page %}
|
||||||
<div class="row q-col-gutter-md justify-center">
|
<div class="row q-col-gutter-md justify-center">
|
||||||
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||||
<q-card class="q-pa-lg">
|
<q-card class="q-pa-lg q-mb-xl">
|
||||||
<q-card-section class="q-pa-none">
|
<q-card-section class="q-pa-none">
|
||||||
<center>
|
<center>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
@ -9,13 +9,53 @@
|
||||||
class="text-grey"
|
class="text-grey"
|
||||||
style="font-size: 10rem"
|
style="font-size: 10rem"
|
||||||
></q-icon>
|
></q-icon>
|
||||||
<h3 class="q-my-none">{{ mint_name }}</h3>
|
<h4 class="q-mt-none q-mb-md">{{ mint_name }}</h4>
|
||||||
<br />
|
<a
|
||||||
|
class="q-my-xl text-white"
|
||||||
|
style="font-size: 1.5rem"
|
||||||
|
href="../wallet?mint_id={{ mint_id }}"
|
||||||
|
>Open wallet</a
|
||||||
|
>
|
||||||
</center>
|
</center>
|
||||||
<h5 class="q-my-none">
|
</q-card-section>
|
||||||
Some data about mint here: <br />* whether its online <br />* Who to
|
</q-card>
|
||||||
contact for support <br />* etc...
|
<q-card class="q-pa-lg q-mb-xl">
|
||||||
</h5>
|
<q-card-section class="q-pa-none">
|
||||||
|
<h5 class="q-my-md">Read the following carefully!</h5>
|
||||||
|
<p>
|
||||||
|
This is a
|
||||||
|
<a href="https://cashu.space/" style="color: white" target="”_blank”"
|
||||||
|
>Cashu</a
|
||||||
|
>
|
||||||
|
mint. Cashu is an ecash system for Bitcoin.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Open this page in your native browser</strong><br />
|
||||||
|
Before you continue to the wallet, make sure to open this page in your
|
||||||
|
device's native browser application (Safari for iOS, Chrome for
|
||||||
|
Android). Do not use Cashu in an embedded browser that opens when you
|
||||||
|
click a link in a messenger.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Add wallet to home screen</strong><br />
|
||||||
|
You can add Cashu to your home screen as a progressive web app (PWA).
|
||||||
|
After opening the wallet in your browser (click the link above), on
|
||||||
|
Android (Chrome), click the menu at the upper right. On iOS (Safari),
|
||||||
|
click the share button. Now press the Add to Home screen button.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Backup your wallet</strong><br />
|
||||||
|
Ecash is a bearer asset. That means losing access to your wallet will
|
||||||
|
make you lose your funds. The wallet stores ecash tokens on your
|
||||||
|
device's database. If you lose the link or delete your your data
|
||||||
|
without backing up, you will lose your tokens. Press the Backup button
|
||||||
|
in the wallet to download a copy of your tokens.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>This service is in BETA</strong> <br />
|
||||||
|
We hold no responsibility for people losing access to funds. Use at
|
||||||
|
your own risk!
|
||||||
|
</p>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -23,16 +23,28 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# @cashu_ext.get("/wallet")
|
||||||
|
# async def wallet(request: Request):
|
||||||
|
# return cashu_renderer().TemplateResponse("cashu/wallet.html", {"request": request})
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.get("/wallet")
|
@cashu_ext.get("/wallet")
|
||||||
async def cashu(request: Request):
|
async def wallet(request: Request, mint_id: str):
|
||||||
return cashu_renderer().TemplateResponse("cashu/wallet.html", {"request": request})
|
return cashu_renderer().TemplateResponse(
|
||||||
|
"cashu/wallet.html",
|
||||||
|
{
|
||||||
|
"request": request,
|
||||||
|
"web_manifest": f"/cashu/manifest/{mint_id}.webmanifest",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.get("/mint/{mintID}")
|
@cashu_ext.get("/mint/{mintID}")
|
||||||
async def cashu(request: Request, mintID):
|
async def cashu(request: Request, mintID):
|
||||||
cashu = await get_cashu(mintID)
|
cashu = await get_cashu(mintID)
|
||||||
return cashu_renderer().TemplateResponse(
|
return cashu_renderer().TemplateResponse(
|
||||||
"cashu/mint.html", {"request": request, "mint_name": cashu.name}
|
"cashu/mint.html",
|
||||||
|
{"request": request, "mint_name": cashu.name, "mint_id": mintID},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -45,29 +57,167 @@ async def manifest(cashu_id: str):
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"short_name": LNBITS_SITE_TITLE,
|
"short_name": "Cashu",
|
||||||
"name": cashu.name + " - " + LNBITS_SITE_TITLE,
|
"name": "Cashu" + " - " + cashu.name,
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": LNBITS_CUSTOM_LOGO
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
|
||||||
if LNBITS_CUSTOM_LOGO
|
|
||||||
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
|
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "900x900",
|
"sizes": "512x512",
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"start_url": "/cashu/" + cashu_id,
|
"id": "/cashu/wallet?mint_id=" + cashu_id,
|
||||||
|
"start_url": "/cashu/wallet?mint_id=" + cashu_id,
|
||||||
"background_color": "#1F2234",
|
"background_color": "#1F2234",
|
||||||
"description": "Bitcoin Lightning tPOS",
|
"description": "Cashu ecash wallet",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"scope": "/cashu/" + cashu_id,
|
"scope": "/cashu/",
|
||||||
"theme_color": "#1F2234",
|
"theme_color": "#1F2234",
|
||||||
|
"protocol_handlers": [
|
||||||
|
{"protocol": "cashu", "url": "&recv_token=%s"},
|
||||||
|
{"protocol": "lightning", "url": "&lightning=%s"},
|
||||||
|
],
|
||||||
"shortcuts": [
|
"shortcuts": [
|
||||||
{
|
{
|
||||||
"name": cashu.name + " - " + LNBITS_SITE_TITLE,
|
"name": "Cashu" + " - " + cashu.name,
|
||||||
"short_name": cashu.name,
|
"short_name": "Cashu",
|
||||||
"description": cashu.name + " - " + LNBITS_SITE_TITLE,
|
"description": "Cashu" + " - " + cashu.name,
|
||||||
"url": "/cashu/" + cashu_id,
|
"url": "/cashu/wallet?mint_id=" + cashu_id,
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-192-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-144-144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-72-72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-48-48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/16.png",
|
||||||
|
"sizes": "16x16",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/20.png",
|
||||||
|
"sizes": "20x20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/29.png",
|
||||||
|
"sizes": "29x29",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/32.png",
|
||||||
|
"sizes": "32x32",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/40.png",
|
||||||
|
"sizes": "40x40",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/50.png",
|
||||||
|
"sizes": "50x50",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/57.png",
|
||||||
|
"sizes": "57x57",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/58.png",
|
||||||
|
"sizes": "58x58",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/60.png",
|
||||||
|
"sizes": "60x60",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/64.png",
|
||||||
|
"sizes": "64x64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/76.png",
|
||||||
|
"sizes": "76x76",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/80.png",
|
||||||
|
"sizes": "80x80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/87.png",
|
||||||
|
"sizes": "87x87",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/100.png",
|
||||||
|
"sizes": "100x100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/114.png",
|
||||||
|
"sizes": "114x114",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/120.png",
|
||||||
|
"sizes": "120x120",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/152.png",
|
||||||
|
"sizes": "152x152",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/167.png",
|
||||||
|
"sizes": "167x167",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/180.png",
|
||||||
|
"sizes": "180x180",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/256.png",
|
||||||
|
"sizes": "256x256",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/1024.png",
|
||||||
|
"sizes": "1024x1024",
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,17 +47,20 @@ from .models import Cashu
|
||||||
# --------- extension imports
|
# --------- extension imports
|
||||||
|
|
||||||
|
|
||||||
LIGHTNING = False
|
LIGHTNING = True
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
############### LNBITS MINTS ###########
|
############### LNBITS MINTS ###########
|
||||||
########################################
|
########################################
|
||||||
|
|
||||||
# todo: use /mints
|
|
||||||
@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
|
@cashu_ext.get("/api/v1/mints", status_code=HTTPStatus.OK)
|
||||||
async def api_cashus(
|
async def api_cashus(
|
||||||
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
|
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Get all mints of this wallet.
|
||||||
|
"""
|
||||||
wallet_ids = [wallet.wallet.id]
|
wallet_ids = [wallet.wallet.id]
|
||||||
if all_wallets:
|
if all_wallets:
|
||||||
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
|
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
|
||||||
|
|
@ -65,8 +68,11 @@ async def api_cashus(
|
||||||
return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
|
return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/cashus", status_code=HTTPStatus.CREATED)
|
@cashu_ext.post("/api/v1/mints", status_code=HTTPStatus.CREATED)
|
||||||
async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)):
|
async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
|
"""
|
||||||
|
Create a new mint for this wallet.
|
||||||
|
"""
|
||||||
cashu_id = urlsafe_short_hash()
|
cashu_id = urlsafe_short_hash()
|
||||||
# generate a new keyset in cashu
|
# generate a new keyset in cashu
|
||||||
keyset = await ledger.load_keyset(cashu_id)
|
keyset = await ledger.load_keyset(cashu_id)
|
||||||
|
|
@ -78,12 +84,35 @@ async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key
|
||||||
return cashu.dict()
|
return cashu.dict()
|
||||||
|
|
||||||
|
|
||||||
|
@cashu_ext.delete("/api/v1/mints/{cashu_id}")
|
||||||
|
async def api_cashu_delete(
|
||||||
|
cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Delete an existing cashu mint.
|
||||||
|
"""
|
||||||
|
cashu = await get_cashu(cashu_id)
|
||||||
|
|
||||||
|
if not cashu:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Cashu mint does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
if cashu.wallet != wallet.wallet.id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu mint."
|
||||||
|
)
|
||||||
|
|
||||||
|
await delete_cashu(cashu_id)
|
||||||
|
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
########### CASHU ENDPOINTS ###########
|
########### CASHU ENDPOINTS ###########
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.get("/api/v1/cashu/{cashu_id}/keys", status_code=HTTPStatus.OK)
|
@cashu_ext.get("/api/v1/{cashu_id}/keys", status_code=HTTPStatus.OK)
|
||||||
async def keys(cashu_id: str = Query(None)) -> dict[int, str]:
|
async def keys(cashu_id: str = Query(None)) -> dict[int, str]:
|
||||||
"""Get the public keys of the mint"""
|
"""Get the public keys of the mint"""
|
||||||
cashu: Union[Cashu, None] = await get_cashu(cashu_id)
|
cashu: Union[Cashu, None] = await get_cashu(cashu_id)
|
||||||
|
|
@ -96,7 +125,20 @@ async def keys(cashu_id: str = Query(None)) -> dict[int, str]:
|
||||||
return ledger.get_keyset(keyset_id=cashu.keyset_id)
|
return ledger.get_keyset(keyset_id=cashu.keyset_id)
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
|
@cashu_ext.get("/api/v1/{cashu_id}/keysets", status_code=HTTPStatus.OK)
|
||||||
|
async def keysets(cashu_id: str = Query(None)) -> dict[str, list[str]]:
|
||||||
|
"""Get the public keys of the mint"""
|
||||||
|
cashu: Union[Cashu, None] = await get_cashu(cashu_id)
|
||||||
|
|
||||||
|
if not cashu:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"keysets": [cashu.keyset_id]}
|
||||||
|
|
||||||
|
|
||||||
|
@cashu_ext.get("/api/v1/{cashu_id}/mint")
|
||||||
async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintResponse:
|
async def request_mint(cashu_id: str = Query(None), 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.
|
||||||
|
|
@ -134,7 +176,7 @@ async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintR
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
|
@cashu_ext.post("/api/v1/{cashu_id}/mint")
|
||||||
async def mint_coins(
|
async def mint_coins(
|
||||||
data: MintRequest,
|
data: MintRequest,
|
||||||
cashu_id: str = Query(None),
|
cashu_id: str = Query(None),
|
||||||
|
|
@ -157,7 +199,7 @@ async def mint_coins(
|
||||||
if invoice is None:
|
if invoice is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND,
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
detail="Mint does not have this invoice.",
|
detail="Mint does not know this invoice.",
|
||||||
)
|
)
|
||||||
if invoice.issued == True:
|
if invoice.issued == True:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -173,27 +215,31 @@ async def mint_coins(
|
||||||
)
|
)
|
||||||
|
|
||||||
status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash)
|
status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash)
|
||||||
# todo: revert to: status.paid != True:
|
|
||||||
if status.paid != True:
|
if status.paid != True:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
|
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await ledger.crud.update_lightning_invoice(
|
|
||||||
db=ledger.db, hash=payment_hash, issued=True
|
|
||||||
)
|
|
||||||
keyset = ledger.keysets.keysets[cashu.keyset_id]
|
keyset = ledger.keysets.keysets[cashu.keyset_id]
|
||||||
|
|
||||||
promises = await ledger._generate_promises(
|
promises = await ledger._generate_promises(
|
||||||
B_s=data.blinded_messages, keyset=keyset
|
B_s=data.blinded_messages, keyset=keyset
|
||||||
)
|
)
|
||||||
|
assert len(promises), HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="No promises returned."
|
||||||
|
)
|
||||||
|
await ledger.crud.update_lightning_invoice(
|
||||||
|
db=ledger.db, hash=payment_hash, issued=True
|
||||||
|
)
|
||||||
|
|
||||||
return promises
|
return promises
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/cashu/{cashu_id}/melt")
|
@cashu_ext.post("/api/v1/{cashu_id}/melt")
|
||||||
async def melt_coins(
|
async def melt_coins(
|
||||||
payload: MeltRequest, cashu_id: str = Query(None)
|
payload: MeltRequest, cashu_id: str = Query(None)
|
||||||
) -> GetMeltResponse:
|
) -> GetMeltResponse:
|
||||||
|
|
@ -211,7 +257,7 @@ async def melt_coins(
|
||||||
# TOKENS
|
# TOKENS
|
||||||
assert all([p.id == cashu.keyset_id for p in proofs]), HTTPException(
|
assert all([p.id == cashu.keyset_id for p in proofs]), HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail="Proofs include tokens from other mint.",
|
detail="Proofs include tokens from another mint.",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert all([ledger._verify_proof(p) for p in proofs]), HTTPException(
|
assert all([ledger._verify_proof(p) for p in proofs]), HTTPException(
|
||||||
|
|
@ -248,19 +294,33 @@ async def melt_coins(
|
||||||
return GetMeltResponse(paid=status.paid, preimage=status.preimage)
|
return GetMeltResponse(paid=status.paid, preimage=status.preimage)
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/check")
|
@cashu_ext.post("/api/v1/{cashu_id}/check")
|
||||||
async def check_spendable(payload: CheckRequest) -> Dict[int, bool]:
|
async def check_spendable(
|
||||||
|
payload: CheckRequest, cashu_id: str = Query(None)
|
||||||
|
) -> Dict[int, bool]:
|
||||||
"""Check whether a secret has been spent already or not."""
|
"""Check whether a secret has been spent already or not."""
|
||||||
|
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."
|
||||||
|
)
|
||||||
return await ledger.check_spendable(payload.proofs)
|
return await ledger.check_spendable(payload.proofs)
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/checkfees")
|
@cashu_ext.post("/api/v1/{cashu_id}/checkfees")
|
||||||
async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
|
async def check_fees(
|
||||||
|
payload: CheckFeesRequest, cashu_id: str = Query(None)
|
||||||
|
) -> CheckFeesResponse:
|
||||||
"""
|
"""
|
||||||
Responds with the fees necessary to pay a Lightning invoice.
|
Responds with the fees necessary to pay a Lightning invoice.
|
||||||
Used by wallets for figuring out the fees they need to supply.
|
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).
|
This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu).
|
||||||
"""
|
"""
|
||||||
|
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."
|
||||||
|
)
|
||||||
invoice_obj = bolt11.decode(payload.pr)
|
invoice_obj = bolt11.decode(payload.pr)
|
||||||
internal_checking_id = await check_internal(invoice_obj.payment_hash)
|
internal_checking_id = await check_internal(invoice_obj.payment_hash)
|
||||||
|
|
||||||
|
|
@ -271,7 +331,7 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
|
||||||
return CheckFeesResponse(fee=fees_msat / 1000)
|
return CheckFeesResponse(fee=fees_msat / 1000)
|
||||||
|
|
||||||
|
|
||||||
@cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
|
@cashu_ext.post("/api/v1/{cashu_id}/split")
|
||||||
async def split(
|
async def split(
|
||||||
payload: SplitRequest, cashu_id: str = Query(None)
|
payload: SplitRequest, cashu_id: str = Query(None)
|
||||||
) -> PostSplitResponse:
|
) -> PostSplitResponse:
|
||||||
|
|
@ -285,15 +345,24 @@ async def split(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
|
||||||
)
|
)
|
||||||
proofs = payload.proofs
|
proofs = payload.proofs
|
||||||
|
|
||||||
|
# !!!!!!! 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 another mint.",
|
||||||
|
)
|
||||||
|
|
||||||
amount = payload.amount
|
amount = payload.amount
|
||||||
outputs = payload.outputs.blinded_messages
|
outputs = payload.outputs.blinded_messages
|
||||||
# backwards compatibility with clients < v0.2.2
|
|
||||||
assert outputs, Exception("no outputs provided.")
|
assert outputs, Exception("no outputs provided.")
|
||||||
split_return = None
|
split_return = None
|
||||||
try:
|
try:
|
||||||
split_return = await ledger.split(proofs, amount, outputs, cashu.keyset_id)
|
keyset = ledger.keysets.keysets[cashu.keyset_id]
|
||||||
|
split_return = await ledger.split(proofs, amount, outputs, keyset)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail=str(exc),
|
detail=str(exc),
|
||||||
)
|
)
|
||||||
|
|
@ -318,24 +387,6 @@ async def split(
|
||||||
# return cashu.dict()
|
# return cashu.dict()
|
||||||
|
|
||||||
|
|
||||||
# @cashu_ext.delete("/api/v1s/{cashu_id}")
|
|
||||||
# async def api_cashu_delete(
|
|
||||||
# cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
|
|
||||||
# ):
|
|
||||||
# cashu = await get_cashu(cashu_id)
|
|
||||||
|
|
||||||
# if not cashu:
|
|
||||||
# raise HTTPException(
|
|
||||||
# status_code=HTTPStatus.NOT_FOUND, detail="Cashu does not exist."
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if cashu.wallet != wallet.wallet.id:
|
|
||||||
# raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu.")
|
|
||||||
|
|
||||||
# await delete_cashu(cashu_id)
|
|
||||||
# raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################################
|
# ########################################
|
||||||
# #################????###################
|
# #################????###################
|
||||||
# ########################################
|
# ########################################
|
||||||
|
|
|
||||||
9
poetry.lock
generated
9
poetry.lock
generated
|
|
@ -123,7 +123,7 @@ uvloop = ["uvloop (>=0.15.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cashu"
|
name = "cashu"
|
||||||
version = "0.4.2"
|
version = "0.5.1"
|
||||||
description = "Ecash wallet and mint with Bitcoin Lightning support"
|
description = "Ecash wallet and mint with Bitcoin Lightning support"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
|
@ -155,6 +155,7 @@ py = {version = "1.11.0", markers = "python_version >= \"3.7\" and python_versio
|
||||||
pycparser = {version = "2.21", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
pycparser = {version = "2.21", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
pydantic = {version = "1.10.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
pydantic = {version = "1.10.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
pyparsing = {version = "3.0.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
pyparsing = {version = "3.0.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
|
pysocks = {version = "1.7.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
pytest = {version = "7.1.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
pytest = {version = "7.1.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
pytest-asyncio = {version = "0.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
pytest-asyncio = {version = "0.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
|
|
@ -1143,7 +1144,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
|
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
|
||||||
content-hash = "7de5e4d432bff49de536b1c90082a6a0821533b3d0fa9d92c22ccaa758d1a65f"
|
content-hash = "c5d3b28864bf6b86385e38f63e3ba16d95804a812773e930b6ed818d4f09938a"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiofiles = [
|
aiofiles = [
|
||||||
|
|
@ -1207,8 +1208,8 @@ black = [
|
||||||
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
|
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
|
||||||
]
|
]
|
||||||
cashu = [
|
cashu = [
|
||||||
{file = "cashu-0.4.2-py3-none-any.whl", hash = "sha256:6d24f5e921c33dae1b6823f5e34feab0d6d5662b56a67c29095d48241163a887"},
|
{file = "cashu-0.5.1-py3-none-any.whl", hash = "sha256:893f6bc098331e73cb6a5d0108c929dc7f2299d3d5405ae3b29e0868d9cd78c9"},
|
||||||
{file = "cashu-0.4.2.tar.gz", hash = "sha256:97564481501cbe163e6be4d3cdd0d52d2841e15b830a0185c3c329657e4b8c36"},
|
{file = "cashu-0.5.1.tar.gz", hash = "sha256:c4533c72a09b0e1439836739653d3d79a7de00a1106e6676cb8f660f894006a7"},
|
||||||
]
|
]
|
||||||
Cerberus = [
|
Cerberus = [
|
||||||
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
|
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ protobuf = "^4.21.6"
|
||||||
Cerberus = "^1.3.4"
|
Cerberus = "^1.3.4"
|
||||||
async-timeout = "^4.0.2"
|
async-timeout = "^4.0.2"
|
||||||
pyln-client = "0.11.1"
|
pyln-client = "0.11.1"
|
||||||
cashu = "0.4.2"
|
cashu = "^0.5.1"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue