From 947dc50d2e6598e52e36d86741d6c12a9cd6b67d Mon Sep 17 00:00:00 2001
From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com>
Date: Mon, 12 Sep 2022 17:14:24 +0200
Subject: [PATCH 0001/1058] readme update
---
lnbits/extensions/boltcards/README.md | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md
index f9c59409..a7302906 100644
--- a/lnbits/extensions/boltcards/README.md
+++ b/lnbits/extensions/boltcards/README.md
@@ -10,18 +10,17 @@ This extension allows you to link your Bolt Card (or other compatible NXP NTAG d
## About the keys
-Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In the empty state they all should be set to zeros (00000000000000000000000000000000). For this extension only two keys need to be set:
+Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In the empty state they all should be set to zeros (00000000000000000000000000000000). For this extension only two keys need to be set, but for the security reasons all five keys should be changed from default (empty) state. The keys directly needed by this extension are:
-One for encrypting the card UID and the counter (p parameter), let's called it meta key, key #01 or K1.
+- One for encrypting the card UID and the counter (p parameter), let's called it meta key, key #01 or K1.
-One for calculating CMAC (c parameter), let's called it file key, key #02 or K2.
+- One for calculating CMAC (c parameter), let's called it file key, key #02 or K2.
-The key #00, K0 (also know as auth key) is skipped to be use as authentification key. Is not needed by this extension, but can be filled in order to write the keys in cooperation with bolt-nfc-android-app.
+The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with bolt-nfc-android-app. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually.
***Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!***
## Setting the card - bolt-nfc-android-app (easy way)
-So far, regarding the keys, the app can only write a new key set on an empty card (with zero keys). **When you write non zero (and 'non debug') keys, they can't be rewrite with this app.** You have to do it on your computer.
- Read the card with the app. Note UID so you can fill it in the extension later.
- Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}`
@@ -35,10 +34,10 @@ So far, regarding the keys, the app can only write a new key set on an empty car
- If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field.
- Advanced Options
- Card Keys (k0, k1, k2) will be automatically generated if not explicitly set.
- - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in debug mode.
- - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead.
+ - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default state.
+ - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead.
- Click CREATE CARD button
-- Click the QR code button next to a card to view its details. You can scan the QR code with the Android app to import the keys.
+- Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys.
- Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. You can then paste this into the Android app to import the keys.
- Tap the NFC card to write the keys to the card.
@@ -48,7 +47,7 @@ Follow the guide.
The URI should be `lnurlw://YOUR-DOMAIN.COM/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000`
-Then fill up the card parameters in the extension. Card Auth key (K0) can be omitted. Initical counter can be 0.
+Then fill up the card parameters in the extension. Card Auth key (K0) can be filled just for the record. Initical counter can be 0.
## Setting the card - android NXP app (hard way)
- If you don't know the card ID, use NXP TagInfo app to find it out.
From e1eb0895890c068a29ee32f4fe7910698522da0e Mon Sep 17 00:00:00 2001
From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com>
Date: Mon, 12 Sep 2022 22:44:22 +0200
Subject: [PATCH 0002/1058] readme minor changes
---
lnbits/extensions/boltcards/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md
index a7302906..8e093f3e 100644
--- a/lnbits/extensions/boltcards/README.md
+++ b/lnbits/extensions/boltcards/README.md
@@ -34,7 +34,7 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio
- If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field.
- Advanced Options
- Card Keys (k0, k1, k2) will be automatically generated if not explicitly set.
- - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default state.
+ - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state.
- GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead.
- Click CREATE CARD button
- Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys.
@@ -47,7 +47,7 @@ Follow the guide.
The URI should be `lnurlw://YOUR-DOMAIN.COM/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000`
-Then fill up the card parameters in the extension. Card Auth key (K0) can be filled just for the record. Initical counter can be 0.
+Then fill up the card parameters in the extension. Card Auth key (K0) can be filled in the extension just for the record. Initical counter can be 0.
## Setting the card - android NXP app (hard way)
- If you don't know the card ID, use NXP TagInfo app to find it out.
From ae06636293cd86f0692744428f7a8d01d51dd1e1 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 15 Sep 2022 18:11:59 +0100
Subject: [PATCH 0003/1058] rebuild pages
From cf849b260cd97e3dbe1d2e11244d67c13962306b Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 16 Sep 2022 13:20:42 +0100
Subject: [PATCH 0004/1058] UI works well
---
lnbits/extensions/cashu/README.md | 11 +
lnbits/extensions/cashu/__init__.py | 25 +
lnbits/extensions/cashu/config.json | 6 +
lnbits/extensions/cashu/crud.py | 50 ++
lnbits/extensions/cashu/migrations.py | 33 +
lnbits/extensions/cashu/models.py | 34 +
lnbits/extensions/cashu/tasks.py | 70 ++
.../cashu/templates/cashu/_api_docs.html | 79 ++
.../cashu/templates/cashu/_cashu.html | 15 +
.../cashu/templates/cashu/index.html | 262 ++++++
.../cashu/templates/cashu/mint.html | 33 +
.../cashu/templates/cashu/wallet.html | 753 ++++++++++++++++++
lnbits/extensions/cashu/views.py | 69 ++
lnbits/extensions/cashu/views_api.py | 160 ++++
14 files changed, 1600 insertions(+)
create mode 100644 lnbits/extensions/cashu/README.md
create mode 100644 lnbits/extensions/cashu/__init__.py
create mode 100644 lnbits/extensions/cashu/config.json
create mode 100644 lnbits/extensions/cashu/crud.py
create mode 100644 lnbits/extensions/cashu/migrations.py
create mode 100644 lnbits/extensions/cashu/models.py
create mode 100644 lnbits/extensions/cashu/tasks.py
create mode 100644 lnbits/extensions/cashu/templates/cashu/_api_docs.html
create mode 100644 lnbits/extensions/cashu/templates/cashu/_cashu.html
create mode 100644 lnbits/extensions/cashu/templates/cashu/index.html
create mode 100644 lnbits/extensions/cashu/templates/cashu/mint.html
create mode 100644 lnbits/extensions/cashu/templates/cashu/wallet.html
create mode 100644 lnbits/extensions/cashu/views.py
create mode 100644 lnbits/extensions/cashu/views_api.py
diff --git a/lnbits/extensions/cashu/README.md b/lnbits/extensions/cashu/README.md
new file mode 100644
index 00000000..8f53b474
--- /dev/null
+++ b/lnbits/extensions/cashu/README.md
@@ -0,0 +1,11 @@
+# Cashu
+
+## Create ecash mint for pegging in/out of ecash
+
+
+
+### Usage
+
+1. Enable extension
+2. Create a Mint
+3. Share wallet
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
new file mode 100644
index 00000000..fa549ad2
--- /dev/null
+++ b/lnbits/extensions/cashu/__init__.py
@@ -0,0 +1,25 @@
+import asyncio
+
+from fastapi import APIRouter
+
+from lnbits.db import Database
+from lnbits.helpers import template_renderer
+from lnbits.tasks import catch_everything_and_restart
+
+db = Database("ext_cashu")
+
+cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["TPoS"])
+
+
+def cashu_renderer():
+ return template_renderer(["lnbits/extensions/cashu/templates"])
+
+
+from .tasks import wait_for_paid_invoices
+from .views import * # noqa
+from .views_api import * # noqa
+
+
+def cashu_start():
+ loop = asyncio.get_event_loop()
+ 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
new file mode 100644
index 00000000..c688b22c
--- /dev/null
+++ b/lnbits/extensions/cashu/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Cashu Ecash",
+ "short_description": "Ecash mints with LN peg in/out",
+ "icon": "approval",
+ "contributors": ["shinobi", "arcbtc", "calle"]
+}
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
new file mode 100644
index 00000000..ce83653f
--- /dev/null
+++ b/lnbits/extensions/cashu/crud.py
@@ -0,0 +1,50 @@
+from typing import List, Optional, Union
+
+from lnbits.helpers import urlsafe_short_hash
+
+from . import db
+from .models import Cashu, Pegs
+
+
+async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
+ cashu_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ cashu_id,
+ wallet_id,
+ data.name,
+ data.tickershort,
+ data.fraction,
+ data.maxsats,
+ data.coins
+ ),
+ )
+
+ cashu = await get_cashu(cashu_id)
+ assert cashu, "Newly created cashu couldn't be retrieved"
+ return cashu
+
+
+async def get_cashu(cashu_id: str) -> Optional[Cashu]:
+ row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
+ return Cashu(**row) if row else None
+
+
+async def get_cashus(wallet_ids: Union[str, List[str]]) -> List[Cashu]:
+ if isinstance(wallet_ids, str):
+ wallet_ids = [wallet_ids]
+
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"SELECT * FROM cashu.cashu WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+
+ return [Cashu(**row) for row in rows]
+
+
+async def delete_cashu(cashu_id: str) -> None:
+ await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,))
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
new file mode 100644
index 00000000..95dc4815
--- /dev/null
+++ b/lnbits/extensions/cashu/migrations.py
@@ -0,0 +1,33 @@
+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 NOT NULL,
+ fraction BOOL,
+ maxsats INT,
+ coins INT
+
+ );
+ """
+ )
+
+ """
+ Initial cashus table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE cashu.pegs (
+ id TEXT PRIMARY KEY,
+ wallet TEXT NOT NULL,
+ inout BOOL NOT NULL,
+ amount INT
+ );
+ """
+ )
+
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
new file mode 100644
index 00000000..0de15362
--- /dev/null
+++ b/lnbits/extensions/cashu/models.py
@@ -0,0 +1,34 @@
+from sqlite3 import Row
+from typing import Optional
+
+from fastapi import Query
+from pydantic import BaseModel
+
+
+class Cashu(BaseModel):
+ id: str = Query(None)
+ name: str = Query(None)
+ wallet: str = Query(None)
+ tickershort: str
+ fraction: bool = Query(None)
+ maxsats: int = Query(0)
+ coins: int = Query(0)
+
+
+ @classmethod
+ def from_row(cls, row: Row) -> "TPoS":
+ return cls(**dict(row))
+
+class Pegs(BaseModel):
+ id: str
+ wallet: str
+ inout: str
+ amount: str
+
+
+ @classmethod
+ def from_row(cls, row: Row) -> "TPoS":
+ return cls(**dict(row))
+
+class PayLnurlWData(BaseModel):
+ lnurl: str
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
new file mode 100644
index 00000000..fe00a591
--- /dev/null
+++ b/lnbits/extensions/cashu/tasks.py
@@ -0,0 +1,70 @@
+import asyncio
+import json
+
+from lnbits.core import db as core_db
+from lnbits.core.crud import create_payment
+from lnbits.core.models import Payment
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.tasks import internal_invoice_queue, register_invoice_listener
+
+from .crud import get_cashu
+
+
+async def wait_for_paid_invoices():
+ invoice_queue = asyncio.Queue()
+ register_invoice_listener(invoice_queue)
+
+ while True:
+ payment = await invoice_queue.get()
+ await on_invoice_paid(payment)
+
+
+async def on_invoice_paid(payment: Payment) -> None:
+ if payment.extra.get("tag") == "cashu" and payment.extra.get("tipSplitted"):
+ # already splitted, ignore
+ return
+
+ # now we make some special internal transfers (from no one to the receiver)
+ cashu = await get_cashu(payment.extra.get("cashuId"))
+ tipAmount = payment.extra.get("tipAmount")
+
+ if tipAmount is None:
+ # no tip amount
+ return
+
+ tipAmount = tipAmount * 1000
+ amount = payment.amount - tipAmount
+
+ # mark the original payment with one extra key, "splitted"
+ # (this prevents us from doing this process again and it's informative)
+ # and reduce it by the amount we're going to send to the producer
+ await core_db.execute(
+ """
+ UPDATE apipayments
+ SET extra = ?, amount = ?
+ WHERE hash = ?
+ AND checking_id NOT LIKE 'internal_%'
+ """,
+ (
+ json.dumps(dict(**payment.extra, tipSplitted=True)),
+ amount,
+ payment.payment_hash,
+ ),
+ )
+
+ # perform the internal transfer using the same payment_hash
+ internal_checking_id = f"internal_{urlsafe_short_hash()}"
+ await create_payment(
+ wallet_id=cashu.tip_wallet,
+ checking_id=internal_checking_id,
+ payment_request="",
+ payment_hash=payment.payment_hash,
+ amount=tipAmount,
+ memo=f"Tip for {payment.memo}",
+ pending=False,
+ extra={"tipSplitted": True},
+ )
+
+ # manually send this for now
+ await internal_invoice_queue.put(internal_checking_id)
+ return
diff --git a/lnbits/extensions/cashu/templates/cashu/_api_docs.html b/lnbits/extensions/cashu/templates/cashu/_api_docs.html
new file mode 100644
index 00000000..7378eb08
--- /dev/null
+++ b/lnbits/extensions/cashu/templates/cashu/_api_docs.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+ GET /cashu/api/v1/cashus
+ Headers
+ {"X-Api-Key": <invoice_key>}
+ Body (application/json)
+
+ Returns 200 OK (application/json)
+
+ [<cashu_object>, ...]
+ Curl example
+ curl -X GET {{ request.base_url }}cashu/api/v1/cashus -H "X-Api-Key:
+ <invoice_key>"
+
+
+
+
+
+
+
+ POST /cashu/api/v1/cashus
+ Headers
+ {"X-Api-Key": <invoice_key>}
+ Body (application/json)
+ {"name": <string>, "currency": <string*ie USD*>}
+
+ Returns 201 CREATED (application/json)
+
+ {"currency": <string>, "id": <string>, "name":
+ <string>, "wallet": <string>}
+ Curl example
+ curl -X POST {{ request.base_url }}cashu/api/v1/cashus -d '{"name":
+ <string>, "currency": <string>}' -H "Content-type:
+ application/json" -H "X-Api-Key: <admin_key>"
+
+
+
+
+
+
+
+
+ DELETE
+ /cashu/api/v1/cashus/<cashu_id>
+ Headers
+ {"X-Api-Key": <admin_key>}
+ Returns 204 NO CONTENT
+
+ Curl example
+ curl -X DELETE {{ request.base_url
+ }}cashu/api/v1/cashus/<cashu_id> -H "X-Api-Key: <admin_key>"
+
+
+
+
+
diff --git a/lnbits/extensions/cashu/templates/cashu/_cashu.html b/lnbits/extensions/cashu/templates/cashu/_cashu.html
new file mode 100644
index 00000000..3c2a38f5
--- /dev/null
+++ b/lnbits/extensions/cashu/templates/cashu/_cashu.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Make Ecash mints with peg in/out to a wallet, that can create and manage ecash.
+
+ Created by
+ Calle .
+
+
+
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html
new file mode 100644
index 00000000..17b2a919
--- /dev/null
+++ b/lnbits/extensions/cashu/templates/cashu/index.html
@@ -0,0 +1,262 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+
+
+
+ New Mint
+
+
+
+
+
+
+
+
Mints
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+ Shareable wallet page
+
+ Shareable mint page
+
+
+
+ {{ (col.name == 'tip_options' && col.value ?
+ JSON.parse(col.value).join(", ") : col.value) }}
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+ {{SITE_TITLE}} Cashu extension
+
+
+
+
+ {% include "cashu/_api_docs.html" %}
+
+ {% include "cashu/_cashu.html" %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use with hedging extension to create a stablecoin!
+
+
+
+
+
+
+
+
+
+
+ Create Mint
+
+ Cancel
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html
new file mode 100644
index 00000000..0f3e0e09
--- /dev/null
+++ b/lnbits/extensions/cashu/templates/cashu/mint.html
@@ -0,0 +1,33 @@
+{% extends "public.html" %} {% block page %}
+
+
+
+
+
+
+ {{ mint_name }}
+
+
+ Some data about mint here: * whether its online * Who to contact for support * etc...
+
+
+
+
+ {% endblock %} {% block scripts %}
+
+
+
+ {% endblock %}
+
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
new file mode 100644
index 00000000..a5d5f371
--- /dev/null
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -0,0 +1,753 @@
+{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Wallet {% endraw %}
+
+{% endblock %} {% block footer %}{% endblock %} {% block page_container %}
+
+
+
+
+
+
+
+
+
+ {% raw %} {{balanceAmount}}
+ {{tickershort}}{% endraw %}
+
+
+
+
+
+
+
+
+ Receive
+
+
+ Send
+
+
+ Peg in/out
+
+
+
+ scan
+
+
+
+
+
+
+
+
+
+
+
Transactions
+
+
{% raw %}
+ {% endraw %}
+ Mint details
+
+ Export to CSV
+
+
+ Show chart
+
+
+
+
+
+
+ {% raw %}
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+ Pending
+
+
+
+
+
+ #{{ props.row.tag }}
+
+
+ {{ props.row.memo }}
+
+
+ {{ props.row.date }}
+ {{ props.row.dateFrom }}
+
+ {% endraw %}
+ {% raw %} {{
+ parseFloat(String(props.row.fsat).replaceAll(",", "")) / 100
+ }}
+
+
+
+ {{ props.row.fsat }}
+
+
+ {{ props.row.fee }}
+
+
+
+
+
+
+
+
+ Invoice waiting to be paid
+
+
+
+ Copy invoice
+ Close
+
+
+
+
+ Payment Received
+
+
+
+
+ Payment Sent
+
+
+
+
+ Outgoing payment pending
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+ {% raw %}
+
+
+
+ {{receive.lnurl.domain}} is requesting an invoice:
+
+ {% endraw %} {% if LNBITS_DENOMINATION != 'sats' %}
+
+ {% else %}
+
+
+ {% endif %}
+
+
+ {% raw %}
+
+
+
+ Withdraw from {{receive.lnurl.domain}}
+
+ Create invoice
+
+ Cancel
+
+
+
+
+
+
+
+ Copy invoice
+ Close
+
+
+ {% endraw %}
+
+
+
+
+
+
+ {% raw %} {{ parseFloat(String(parse.invoice.fsat).replaceAll(",",
+ "")) / 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
+
+
+ {{ parse.invoice.fsat }}{% endraw %} {{LNBITS_DENOMINATION}} {%
+ raw %}
+
+
+
+ Description: {{ parse.invoice.description }}
+ Expire date: {{ parse.invoice.expireDate }}
+ Hash: {{ parse.invoice.hash }}
+
+ {% endraw %}
+
+ Pay
+ Cancel
+
+
+ Not enough funds!
+ Cancel
+
+
+
+ {% raw %}
+
+
+ Authenticate with {{ parse.lnurlauth.domain }} ?
+
+
+
+ For every website and for every LNbits wallet, a new keypair
+ will be deterministically generated so your identity can't be
+ tied to your LNbits wallet or linked across websites. No other
+ data will be shared with {{ parse.lnurlauth.domain }}.
+
+ Your public key for {{ parse.lnurlauth.domain }} is:
+
+ {{ parse.lnurlauth.pubkey }}
+
+
+ Login
+ Cancel
+
+
+ {% endraw %}
+
+
+
+
+
+
+ Read
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Warning
+
+ BOOKMARK THIS PAGE! If only mobile you can also click the 3 dots
+ and "Save to homescreen"/"Install app" !
+
+
+ Ecash is a bearer asset, meaning you have the funds saved on this
+ page, losing the page without exporting the page will mean you will
+ lose the funds.
+
+
+ Copy wallet URL
+ I understand
+
+
+
+
+
+
+{% endblock %} {% block styles %}
+
+{% endblock %} {% block scripts %}
+
+{% endblock %}
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
new file mode 100644
index 00000000..4ac1f1ce
--- /dev/null
+++ b/lnbits/extensions/cashu/views.py
@@ -0,0 +1,69 @@
+from http import HTTPStatus
+
+from fastapi import Request
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.exceptions import HTTPException
+from starlette.responses import HTMLResponse
+
+from lnbits.core.models import User
+from lnbits.decorators import check_user_exists
+from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE
+
+from . import cashu_ext, cashu_renderer
+from .crud import get_cashu
+
+templates = Jinja2Templates(directory="templates")
+
+
+@cashu_ext.get("/", response_class=HTMLResponse)
+async def index(request: Request, user: User = Depends(check_user_exists)):
+ return cashu_renderer().TemplateResponse(
+ "cashu/index.html", {"request": request, "user": user.dict()}
+ )
+
+
+@cashu_ext.get("/wallet")
+async def cashu(request: Request):
+ return cashu_renderer().TemplateResponse("cashu/wallet.html",{"request": request})
+
+@cashu_ext.get("/mint/{mintID}")
+async def cashu(request: Request, mintID):
+ cashu = await get_cashu(mintID)
+ return cashu_renderer().TemplateResponse("cashu/mint.html",{"request": request, "mint_name": cashu.name})
+
+@cashu_ext.get("/manifest/{cashu_id}.webmanifest")
+async def manifest(cashu_id: str):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ )
+
+ return {
+ "short_name": LNBITS_SITE_TITLE,
+ "name": cashu.name + " - " + LNBITS_SITE_TITLE,
+ "icons": [
+ {
+ "src": LNBITS_CUSTOM_LOGO
+ if LNBITS_CUSTOM_LOGO
+ else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
+ "type": "image/png",
+ "sizes": "900x900",
+ }
+ ],
+ "start_url": "/cashu/" + cashu_id,
+ "background_color": "#1F2234",
+ "description": "Bitcoin Lightning tPOS",
+ "display": "standalone",
+ "scope": "/cashu/" + cashu_id,
+ "theme_color": "#1F2234",
+ "shortcuts": [
+ {
+ "name": cashu.name + " - " + LNBITS_SITE_TITLE,
+ "short_name": cashu.name,
+ "description": cashu.name + " - " + LNBITS_SITE_TITLE,
+ "url": "/cashu/" + cashu_id,
+ }
+ ],
+ }
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
new file mode 100644
index 00000000..0b16e4ba
--- /dev/null
+++ b/lnbits/extensions/cashu/views_api.py
@@ -0,0 +1,160 @@
+from http import HTTPStatus
+
+import httpx
+from fastapi import Query
+from fastapi.params import Depends
+from lnurl import decode as decode_lnurl
+from loguru import logger
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_user
+from lnbits.core.services import create_invoice
+from lnbits.core.views.api import api_payment
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
+
+from . import cashu_ext
+from .crud import create_cashu, delete_cashu, get_cashu, get_cashus
+from .models import Cashu, Pegs, PayLnurlWData
+
+
+@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
+async def api_cashus(
+ all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
+):
+ wallet_ids = [wallet.wallet.id]
+ if all_wallets:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
+
+
+@cashu_ext.post("/api/v1/cashus", status_code=HTTPStatus.CREATED)
+async def api_cashu_create(
+ data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
+):
+ cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
+ logger.debug(cashu)
+ return cashu.dict()
+
+
+@cashu_ext.delete("/api/v1/cashus/{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="TPoS does not exist."
+ )
+
+ if cashu.wallet != wallet.wallet.id:
+ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your TPoS.")
+
+ await delete_cashu(cashu_id)
+ raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
+
+
+@cashu_ext.post("/api/v1/cashus/{cashu_id}/invoices", status_code=HTTPStatus.CREATED)
+async def api_cashu_create_invoice(
+ amount: int = Query(..., ge=1), tipAmount: int = None, cashu_id: str = None
+):
+ cashu = await get_cashu(cashu_id)
+
+ if not cashu:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ )
+
+ if tipAmount:
+ amount += tipAmount
+
+ try:
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=cashu.wallet,
+ amount=amount,
+ memo=f"{cashu.name}",
+ extra={"tag": "cashu", "tipAmount": tipAmount, "cashuId": cashu_id},
+ )
+ except Exception as e:
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+ return {"payment_hash": payment_hash, "payment_request": payment_request}
+
+
+@cashu_ext.post(
+ "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay", status_code=HTTPStatus.OK
+)
+async def api_cashu_pay_invoice(
+ lnurl_data: PayLnurlWData, payment_request: str = None, cashu_id: str = None
+):
+ cashu = await get_cashu(cashu_id)
+
+ if not cashu:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ )
+
+ lnurl = (
+ lnurl_data.lnurl.replace("lnurlw://", "")
+ .replace("lightning://", "")
+ .replace("LIGHTNING://", "")
+ .replace("lightning:", "")
+ .replace("LIGHTNING:", "")
+ )
+
+ if lnurl.lower().startswith("lnurl"):
+ lnurl = decode_lnurl(lnurl)
+ else:
+ lnurl = "https://" + lnurl
+
+ async with httpx.AsyncClient() as client:
+ try:
+ r = await client.get(lnurl, follow_redirects=True)
+ if r.is_error:
+ lnurl_response = {"success": False, "detail": "Error loading"}
+ else:
+ resp = r.json()
+ if resp["tag"] != "withdrawRequest":
+ lnurl_response = {"success": False, "detail": "Wrong tag type"}
+ else:
+ r2 = await client.get(
+ resp["callback"],
+ follow_redirects=True,
+ params={
+ "k1": resp["k1"],
+ "pr": payment_request,
+ },
+ )
+ resp2 = r2.json()
+ if r2.is_error:
+ lnurl_response = {
+ "success": False,
+ "detail": "Error loading callback",
+ }
+ elif resp2["status"] == "ERROR":
+ lnurl_response = {"success": False, "detail": resp2["reason"]}
+ else:
+ lnurl_response = {"success": True, "detail": resp2}
+ except (httpx.ConnectError, httpx.RequestError):
+ lnurl_response = {"success": False, "detail": "Unexpected error occurred"}
+
+ return lnurl_response
+
+
+@cashu_ext.get(
+ "/api/v1/cashus/{cashu_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
+)
+async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ )
+ try:
+ status = await api_payment(payment_hash)
+
+ except Exception as exc:
+ logger.error(exc)
+ return {"paid": False}
+ return status
From 3660d96295ea75be08cd1e55b7899da60cd66eb9 Mon Sep 17 00:00:00 2001
From: Gene Takavic
Date: Tue, 20 Sep 2022 15:53:57 +0200
Subject: [PATCH 0005/1058] rename setting app + delete msg
---
lnbits/extensions/boltcards/README.md | 6 +++---
lnbits/extensions/boltcards/static/js/index.js | 2 +-
lnbits/extensions/boltcards/templates/boltcards/index.html | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md
index 8e093f3e..c6d12311 100644
--- a/lnbits/extensions/boltcards/README.md
+++ b/lnbits/extensions/boltcards/README.md
@@ -6,7 +6,7 @@ This extension allows you to link your Bolt Card (or other compatible NXP NTAG d
**Disclaimer:** ***Use this only if you either know what you are doing or are a reckless lightning pioneer. Only you are responsible for all your sats, cards and other devices. Always backup all your card keys!***
-***In order to use this extension you need to be able to setup your own card.*** That means writing a URL template pointing to your LNBits instance, configuring some SUN (SDM) settings and optionally changing the card's keys. There's a [guide](https://www.whitewolftech.com/articles/payment-card/) to set it up with a card reader connected to your computer. It can be done (without setting the keys) with [TagWriter app by NXP](https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter) Android app. Last but not least, an OSS android app by name [bolt-nfc-android-app](https://github.com/boltcard/bolt-nfc-android-app) is being developed for these purposes. It's available from Google Play [here](https://play.google.com/store/apps/details?id=com.lightningnfcapp).
+***In order to use this extension you need to be able to setup your own card.*** That means writing a URL template pointing to your LNBits instance, configuring some SUN (SDM) settings and optionally changing the card's keys. There's a [guide](https://www.whitewolftech.com/articles/payment-card/) to set it up with a card reader connected to your computer. It can be done (without setting the keys) with [TagWriter app by NXP](https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter) Android app. Last but not least, an OSS android app by name [Boltcard NFC Card Creator](https://github.com/boltcard/bolt-nfc-android-app) is being developed for these purposes. It's available from Google Play [here](https://play.google.com/store/apps/details?id=com.lightningnfcapp).
## About the keys
@@ -16,11 +16,11 @@ Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In th
- One for calculating CMAC (c parameter), let's called it file key, key #02 or K2.
-The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with bolt-nfc-android-app. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually.
+The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with Boltcard NFC Card Creator. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually.
***Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!***
-## Setting the card - bolt-nfc-android-app (easy way)
+## Setting the card - Boltcard NFC Card Creator (easy way)
- Read the card with the app. Note UID so you can fill it in the extension later.
- Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}`
diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js
index 11df222a..2ecde39d 100644
--- a/lnbits/extensions/boltcards/static/js/index.js
+++ b/lnbits/extensions/boltcards/static/js/index.js
@@ -398,7 +398,7 @@ new Vue({
let cards = _.findWhere(this.cards, {id: cardId})
LNbits.utils
- .confirmDialog('Are you sure you want to delete this card')
+ .confirmDialog('Are you sure you want to delete this card? Without access to the card keys you won\'t be able to reset them in the future!')
.onOk(function () {
LNbits.api
.request(
diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html
index 55cc1e5e..3e07024c 100644
--- a/lnbits/extensions/boltcards/templates/boltcards/index.html
+++ b/lnbits/extensions/boltcards/templates/boltcards/index.html
@@ -370,7 +370,7 @@
bolt-nfc-android-app Boltcard NFC Card Creator)
From a9b6dd3d30711296a7d9134dc59caf6d5ccda154 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:16:38 +0100
Subject: [PATCH 0006/1058] Broke, started added private/public key
---
lnbits/extensions/cashu/crud.py | 26 +++++++++++++++++++++++---
lnbits/extensions/cashu/migrations.py | 5 +++--
lnbits/extensions/cashu/models.py | 3 ++-
lnbits/extensions/cashu/views_api.py | 12 +++++++++++-
4 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index ce83653f..f50da111 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -5,13 +5,20 @@ from lnbits.helpers import urlsafe_short_hash
from . import db
from .models import Cashu, Pegs
+from embit import script
+from embit import ec
+from embit.networks import NETWORKS
+from binascii import unhexlify, hexlify
async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
cashu_id = urlsafe_short_hash()
+ prv = ec.PrivateKey.from_wif(urlsafe_short_hash())
+ pub = prv.get_public_key()
+
await db.execute(
"""
- INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, prvkey, pubkey)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
cashu_id,
@@ -20,7 +27,9 @@ async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
data.tickershort,
data.fraction,
data.maxsats,
- data.coins
+ data.coins,
+ prv,
+ pub
),
)
@@ -29,6 +38,17 @@ async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
return cashu
+async def update_cashu_keys(cashu_id, wif: str = None) -> Optional[Cashu]:
+ if not wif:
+ prv = ec.PrivateKey.from_wif(urlsafe_short_hash())
+ else:
+ prv = ec.PrivateKey.from_wif(wif)
+ pub = prv.get_public_key()
+ await db.execute("UPDATE cashu.cashu SET prv = ?, pub = ? WHERE id = ?", (hexlify(prv.serialize()), hexlify(pub.serialize()), cashu_id))
+ row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
+ return Cashu(**row) if row else None
+
+
async def get_cashu(cashu_id: str) -> Optional[Cashu]:
row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
return Cashu(**row) if row else None
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index 95dc4815..53420062 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -11,8 +11,9 @@ async def m001_initial(db):
tickershort TEXT NOT NULL,
fraction BOOL,
maxsats INT,
- coins INT
-
+ coins INT,
+ prvkey TEXT NOT NULL,
+ pubkey TEXT NOT NULL
);
"""
)
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 0de15362..892abdf1 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -13,7 +13,8 @@ class Cashu(BaseModel):
fraction: bool = Query(None)
maxsats: int = Query(0)
coins: int = Query(0)
-
+ prvkey: str = Query(None)
+ pubkey: str = Query(None)
@classmethod
def from_row(cls, row: Row) -> "TPoS":
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 0b16e4ba..49383945 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -13,7 +13,7 @@ from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import cashu_ext
-from .crud import create_cashu, delete_cashu, get_cashu, get_cashus
+from .crud import create_cashu, delete_cashu, get_cashu, get_cashus, update_cashu_keys
from .models import Cashu, Pegs, PayLnurlWData
@@ -36,6 +36,16 @@ async def api_cashu_create(
logger.debug(cashu)
return cashu.dict()
+@cashu_ext.post("/api/v1/cashus/upodatekeys", status_code=HTTPStatus.CREATED)
+async def api_cashu_update_keys(
+ data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
+):
+ cashu = await get_cashu(data.id)
+
+ cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
+ logger.debug(cashu)
+ return cashu.dict()
+
@cashu_ext.delete("/api/v1/cashus/{cashu_id}")
async def api_cashu_delete(
From 632d35682d84c981b926ef47f8e6fc9d274b948e Mon Sep 17 00:00:00 2001
From: Gene Takavic
Date: Fri, 23 Sep 2022 15:17:21 +0200
Subject: [PATCH 0007/1058] payment notification webhook
---
lnbits/extensions/boltcards/crud.py | 6 +++--
lnbits/extensions/boltcards/lnurl.py | 23 ++++++++++++++++++-
lnbits/extensions/boltcards/migrations.py | 8 +++++++
lnbits/extensions/boltcards/models.py | 2 ++
.../extensions/boltcards/static/js/index.js | 8 +++++--
.../boltcards/templates/boltcards/index.html | 12 +++++++++-
6 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/lnbits/extensions/boltcards/crud.py b/lnbits/extensions/boltcards/crud.py
index c541346e..39ee3f40 100644
--- a/lnbits/extensions/boltcards/crud.py
+++ b/lnbits/extensions/boltcards/crud.py
@@ -27,9 +27,10 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card:
k0,
k1,
k2,
- otp
+ otp,
+ webhook_url
)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
card_id,
@@ -45,6 +46,7 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card:
data.k1,
data.k2,
secrets.token_hex(16),
+ data.webhook_url,
),
)
card = await get_card(card_id)
diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py
index 6fb9ad8d..be3e09d8 100644
--- a/lnbits/extensions/boltcards/lnurl.py
+++ b/lnbits/extensions/boltcards/lnurl.py
@@ -8,6 +8,7 @@ from io import BytesIO
from typing import Optional
from urllib.parse import urlparse
+import httpx
from embit import bech32, compact
from fastapi import Request
from fastapi.param_functions import Query
@@ -119,12 +120,32 @@ async def lnurl_callback(
invoice = bolt11.decode(pr)
hit = await spend_hit(id=hit.id, amount=int(invoice.amount_msat / 1000))
try:
- await pay_invoice(
+ payment_hash = await pay_invoice(
wallet_id=card.wallet,
payment_request=pr,
max_sat=card.tx_limit,
extra={"tag": "boltcard", "tag": hit.id},
)
+
+ if card.webhook_url:
+ async with httpx.AsyncClient() as client:
+ try:
+ r = await client.post(
+ card.webhook_url,
+ json={
+ "notification": "card_payment",
+ "payment_hash": payment_hash,
+ "payment_request": pr,
+ "card_external_id": card.external_id,
+ "card_name": card.card_name,
+ "amount": int(invoice.amount_msat / 1000),
+ },
+ timeout=40,
+ )
+ except Exception as exc:
+ # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
+ logger.error("Caught exception when dispatching webhook url:", exc)
+
return {"status": "OK"}
except:
return {"status": "ERROR", "reason": f"Payment failed"}
diff --git a/lnbits/extensions/boltcards/migrations.py b/lnbits/extensions/boltcards/migrations.py
index 08126013..25a59fdb 100644
--- a/lnbits/extensions/boltcards/migrations.py
+++ b/lnbits/extensions/boltcards/migrations.py
@@ -58,3 +58,11 @@ async def m001_initial(db):
);
"""
)
+
+
+async def m002_add_webhook(db):
+ await db.execute(
+ """
+ ALTER TABLE boltcards.cards ADD COLUMN webhook_url TEXT NOT NULL DEFAULT '';
+ """
+ )
diff --git a/lnbits/extensions/boltcards/models.py b/lnbits/extensions/boltcards/models.py
index 47ca1df0..8e6f77c9 100644
--- a/lnbits/extensions/boltcards/models.py
+++ b/lnbits/extensions/boltcards/models.py
@@ -30,6 +30,7 @@ class Card(BaseModel):
prev_k1: str
prev_k2: str
otp: str
+ webhook_url: str
time: int
def from_row(cls, row: Row) -> "Card":
@@ -56,6 +57,7 @@ class CreateCardData(BaseModel):
prev_k0: str = Query(ZERO_KEY)
prev_k1: str = Query(ZERO_KEY)
prev_k2: str = Query(ZERO_KEY)
+ webhook_url: str = Query(...)
class Hit(BaseModel):
diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js
index 2ecde39d..e13c14fb 100644
--- a/lnbits/extensions/boltcards/static/js/index.js
+++ b/lnbits/extensions/boltcards/static/js/index.js
@@ -23,6 +23,7 @@ new Vue({
cardDialog: {
show: false,
data: {
+ webhook_url: '',
counter: 1,
k0: '',
k1: '',
@@ -270,7 +271,8 @@ new Vue({
k1: card.k1,
k2: card.k2,
k3: card.k1,
- k4: card.k2
+ k4: card.k2,
+ webhook_url: card.webhook_url
}
this.qrCodeDialog.show = true
},
@@ -398,7 +400,9 @@ new Vue({
let cards = _.findWhere(this.cards, {id: cardId})
LNbits.utils
- .confirmDialog('Are you sure you want to delete this card? Without access to the card keys you won\'t be able to reset them in the future!')
+ .confirmDialog(
+ "Are you sure you want to delete this card? Without access to the card keys you won't be able to reset them in the future!"
+ )
.onOk(function () {
LNbits.api
.request(
diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html
index 3e07024c..f795e454 100644
--- a/lnbits/extensions/boltcards/templates/boltcards/index.html
+++ b/lnbits/extensions/boltcards/templates/boltcards/index.html
@@ -283,7 +283,7 @@
v-model="toggleAdvanced"
label="Show advanced options"
>
-
+
Zero if you don't know.
+
+
Lock key: {{ qrCodeDialog.data.k0 }}
Meta key: {{ qrCodeDialog.data.k1 }}
File key: {{ qrCodeDialog.data.k2 }}
+ Notification webhook: {{ qrCodeDialog.data.webhook_url
+ }}
Date: Fri, 23 Sep 2022 16:19:58 +0200
Subject: [PATCH 0008/1058] Update README.md
Updated for Boltcard NFC Card Creator v0.1.1
---
lnbits/extensions/boltcards/README.md | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md
index c6d12311..5140ecc2 100644
--- a/lnbits/extensions/boltcards/README.md
+++ b/lnbits/extensions/boltcards/README.md
@@ -21,10 +21,7 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio
***Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!***
## Setting the card - Boltcard NFC Card Creator (easy way)
-
-- Read the card with the app. Note UID so you can fill it in the extension later.
-- Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}`
- - `{external_id}` should be replaced with the External ID found in the LNBits dialog.
+Updated for v0.1.1
- Add new card in the extension.
- Set a max sats per transaction. Any transaction greater than this amount will be rejected.
@@ -32,14 +29,16 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio
- Set a card name. This is just for your reference inside LNBits.
- Set the card UID. This is the unique identifier on your NFC card and is 7 bytes.
- If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field.
+ - Otherwise read it with the Android app (Advanced -> Read NFC) and paste it to the field.
- Advanced Options
- Card Keys (k0, k1, k2) will be automatically generated if not explicitly set.
- - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state.
- - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead.
+ - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state (this is unsecure).
+ - GENERATE KEY button fill the keys randomly.
- Click CREATE CARD button
-- Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys.
-- Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. You can then paste this into the Android app to import the keys.
-- Tap the NFC card to write the keys to the card.
+- Click the QR code button next to a card to view its details. Backup the keys now! They'll be comfortable in your password manager.
+ - Now you can scan the QR code with the Android app (Create Bolt Card -> SCAN QR CODE).
+ - Or you can Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. Then paste it into the Android app (Create Bolt Card -> PASTE AUTH URL).
+- Click WRITE CARD NOW and tap the NFC card to set it up. DO NOT REMOVE THE CARD PREMATURELY!
## Setting the card - computer (hard way)
@@ -69,4 +68,4 @@ Then fill up the card parameters in the extension. Card Auth key (K0) can be fil
- Save & Write
- Scan with compatible Wallet
-This app afaik cannot change the keys. If you cannot change them any other way, leave them empty in the extension dialog and remember you're not secure. Card Auth key (K0) can be omitted anyway. Initical counter can be 0.
+This app afaik cannot change the keys. If you cannot change them any other way, leave them empty in the extension dialog and remember you're not secured. Card Auth key (K0) can be omitted anyway. Initical counter can be 0.
From ea2d66fbd628681618dbd411f544f63ee8692a5f Mon Sep 17 00:00:00 2001
From: Gene Takavic
Date: Fri, 23 Sep 2022 17:11:19 +0200
Subject: [PATCH 0009/1058] refund notification
---
lnbits/extensions/boltcards/tasks.py | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/boltcards/tasks.py b/lnbits/extensions/boltcards/tasks.py
index 1b51c98b..27929efc 100644
--- a/lnbits/extensions/boltcards/tasks.py
+++ b/lnbits/extensions/boltcards/tasks.py
@@ -2,12 +2,13 @@ import asyncio
import json
import httpx
+from loguru import logger
from lnbits.core import db as core_db
from lnbits.core.models import Payment
from lnbits.tasks import register_invoice_listener
-from .crud import create_refund, get_hit
+from .crud import create_refund, get_card, get_hit
async def wait_for_paid_invoices():
@@ -34,6 +35,25 @@ async def on_invoice_paid(payment: Payment) -> None:
)
await mark_webhook_sent(payment, 1)
+ card = await get_card(hit.card_id)
+ if card.webhook_url:
+ async with httpx.AsyncClient() as client:
+ try:
+ r = await client.post(
+ card.webhook_url,
+ json={
+ "notification": "card_refund",
+ "payment_hash": payment.payment_hash,
+ "payment_request": payment.bolt11,
+ "card_external_id": card.external_id,
+ "card_name": card.card_name,
+ "amount": int(payment.amount / 1000),
+ },
+ timeout=40,
+ )
+ except Exception as exc:
+ logger.error("Caught exception when dispatching webhook url:", exc)
+
async def mark_webhook_sent(payment: Payment, status: int) -> None:
payment.extra["wh_status"] = status
From 8b3af03519c06437325e32e78a71febf9f91d3a1 Mon Sep 17 00:00:00 2001
From: Gene Takavic
Date: Sun, 25 Sep 2022 17:01:22 +0200
Subject: [PATCH 0010/1058] webhook to pay_invoice
---
lnbits/core/services.py | 31 ++++++++++++++++-
lnbits/extensions/boltcards/lnurl.py | 42 ++++++++----------------
lnbits/extensions/boltcards/views_api.py | 5 ---
lnbits/extensions/withdraw/lnurl.py | 30 +++++++----------
4 files changed, 56 insertions(+), 52 deletions(-)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 10693f4b..aeb4f938 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -2,7 +2,7 @@ import asyncio
import json
from binascii import unhexlify
from io import BytesIO
-from typing import Dict, Optional, Tuple
+from typing import Dict, Optional, Tuple, Union
from urllib.parse import parse_qs, urlparse
import httpx
@@ -102,6 +102,7 @@ async def pay_invoice(
extra: Optional[Dict] = None,
description: str = "",
conn: Optional[Connection] = None,
+ webhook: Optional[Union[str, tuple]] = None,
) -> str:
"""
Pay a Lightning invoice.
@@ -231,6 +232,34 @@ async def pay_invoice(
f"didn't receive checking_id from backend, payment may be stuck in database: {temp_id}"
)
+ if type(webhook) is str:
+ webhook_url = webhook
+ elif type(webhook) is tuple:
+ webhook_url = webhook[0]
+ additionals = webhook[1]
+ else:
+ webhook_url = None
+
+ if webhook_url:
+ async with httpx.AsyncClient() as client:
+ try:
+ json = {
+ "payment_hash": invoice.payment_hash,
+ "payment_request": payment_request,
+ "amount": int(invoice.amount_msat / 1000),
+ }
+ if type(additionals) is dict:
+ json.update(additionals)
+
+ r = await client.post(
+ webhook_url,
+ json=json,
+ timeout=40,
+ )
+ except Exception as exc:
+ # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
+ logger.error("Caught exception when dispatching webhook url:", exc)
+
return invoice.payment_hash
diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py
index be3e09d8..320b1666 100644
--- a/lnbits/extensions/boltcards/lnurl.py
+++ b/lnbits/extensions/boltcards/lnurl.py
@@ -1,19 +1,12 @@
-import base64
-import hashlib
-import hmac
import json
import secrets
from http import HTTPStatus
-from io import BytesIO
-from typing import Optional
from urllib.parse import urlparse
-import httpx
from embit import bech32, compact
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends, Query
-from lnurl import Lnurl, LnurlWithdrawResponse
from lnurl import encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from loguru import logger
@@ -34,7 +27,6 @@ from .crud import (
get_hit,
get_hits_today,
spend_hit,
- update_card,
update_card_counter,
update_card_otp,
)
@@ -120,32 +112,26 @@ async def lnurl_callback(
invoice = bolt11.decode(pr)
hit = await spend_hit(id=hit.id, amount=int(invoice.amount_msat / 1000))
try:
- payment_hash = await pay_invoice(
+ webhook = (
+ (
+ card.webhook_url,
+ {
+ "notification": "card_payment",
+ "card_external_id": card.external_id,
+ "card_name": card.card_name,
+ },
+ )
+ if card.webhook_url
+ else None
+ )
+ await pay_invoice(
wallet_id=card.wallet,
payment_request=pr,
max_sat=card.tx_limit,
extra={"tag": "boltcard", "tag": hit.id},
+ webhook=webhook,
)
- if card.webhook_url:
- async with httpx.AsyncClient() as client:
- try:
- r = await client.post(
- card.webhook_url,
- json={
- "notification": "card_payment",
- "payment_hash": payment_hash,
- "payment_request": pr,
- "card_external_id": card.external_id,
- "card_name": card.card_name,
- "amount": int(invoice.amount_msat / 1000),
- },
- timeout=40,
- )
- except Exception as exc:
- # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
- logger.error("Caught exception when dispatching webhook url:", exc)
-
return {"status": "OK"}
except:
return {"status": "ERROR", "reason": f"Payment failed"}
diff --git a/lnbits/extensions/boltcards/views_api.py b/lnbits/extensions/boltcards/views_api.py
index 7b8357cf..2fc11dbc 100644
--- a/lnbits/extensions/boltcards/views_api.py
+++ b/lnbits/extensions/boltcards/views_api.py
@@ -12,21 +12,16 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import boltcards_ext
from .crud import (
create_card,
- create_hit,
delete_card,
enable_disable_card,
get_card,
- get_card_by_otp,
get_card_by_uid,
get_cards,
get_hits,
get_refunds,
update_card,
- update_card_counter,
- update_card_otp,
)
from .models import CreateCardData
-from .nxp424 import decryptSUN, getSunMAC
@boltcards_ext.get("/api/v1/cards")
diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py
index 18a99599..3379fd17 100644
--- a/lnbits/extensions/withdraw/lnurl.py
+++ b/lnbits/extensions/withdraw/lnurl.py
@@ -3,7 +3,6 @@ import traceback
from datetime import datetime
from http import HTTPStatus
-import httpx
import shortuuid # type: ignore
from fastapi import HTTPException
from fastapi.param_functions import Query
@@ -115,29 +114,24 @@ async def api_lnurl_callback(
payment_request = pr
- payment_hash = await pay_invoice(
+ webhook = (
+ (
+ link.webhook_url,
+ {
+ "lnurlw": link.id,
+ },
+ )
+ if link.webhook_url
+ else None
+ )
+ await pay_invoice(
wallet_id=link.wallet,
payment_request=payment_request,
max_sat=link.max_withdrawable,
extra={"tag": "withdraw"},
+ webhook=webhook,
)
- if link.webhook_url:
- async with httpx.AsyncClient() as client:
- try:
- r = await client.post(
- link.webhook_url,
- json={
- "payment_hash": payment_hash,
- "payment_request": payment_request,
- "lnurlw": link.id,
- },
- timeout=40,
- )
- except Exception as exc:
- # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
- logger.error("Caught exception when dispatching webhook url:", exc)
-
return {"status": "OK"}
except Exception as e:
From 0bede387f5ed5620781c50b689fc7016ad5d15af Mon Sep 17 00:00:00 2001
From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com>
Date: Sun, 25 Sep 2022 18:11:25 +0200
Subject: [PATCH 0011/1058] webhook to pay_invoice/fix
---
lnbits/core/services.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index aeb4f938..4f937ec3 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -232,6 +232,7 @@ async def pay_invoice(
f"didn't receive checking_id from backend, payment may be stuck in database: {temp_id}"
)
+ additionals = None
if type(webhook) is str:
webhook_url = webhook
elif type(webhook) is tuple:
From 5c6cd70d3bf08720a094c4aac263f53c583664e9 Mon Sep 17 00:00:00 2001
From: Gene Takavic
Date: Mon, 26 Sep 2022 11:47:00 +0200
Subject: [PATCH 0012/1058] disabling card with just otp which is also part of
notify
---
lnbits/extensions/boltcards/crud.py | 10 ++++++++--
lnbits/extensions/boltcards/lnurl.py | 3 +++
lnbits/extensions/boltcards/views_api.py | 18 ++++++++++++++++++
3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/boltcards/crud.py b/lnbits/extensions/boltcards/crud.py
index 39ee3f40..0724ea04 100644
--- a/lnbits/extensions/boltcards/crud.py
+++ b/lnbits/extensions/boltcards/crud.py
@@ -114,8 +114,14 @@ async def get_card_by_external_id(external_id: str) -> Optional[Card]:
return Card.parse_obj(card)
-async def get_card_by_otp(otp: str) -> Optional[Card]:
- row = await db.fetchone("SELECT * FROM boltcards.cards WHERE otp = ?", (otp,))
+async def get_card_by_otp(otp: str, half: bool = False) -> Optional[Card]:
+ if half and len(otp) == 16:
+ otp = "%" + otp
+ row = await db.fetchone(
+ "SELECT * FROM boltcards.cards WHERE otp LIKE ?", (otp,)
+ )
+ else:
+ row = await db.fetchone("SELECT * FROM boltcards.cards WHERE otp = ?", (otp,))
if not row:
return None
diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py
index 320b1666..064bde2c 100644
--- a/lnbits/extensions/boltcards/lnurl.py
+++ b/lnbits/extensions/boltcards/lnurl.py
@@ -119,6 +119,9 @@ async def lnurl_callback(
"notification": "card_payment",
"card_external_id": card.external_id,
"card_name": card.card_name,
+ "card_otp": card.otp[
+ -16:
+ ], # actually only half of the OTP is sent (full otp reveals the keys)
},
)
if card.webhook_url
diff --git a/lnbits/extensions/boltcards/views_api.py b/lnbits/extensions/boltcards/views_api.py
index 2fc11dbc..d7f5cf7c 100644
--- a/lnbits/extensions/boltcards/views_api.py
+++ b/lnbits/extensions/boltcards/views_api.py
@@ -15,11 +15,13 @@ from .crud import (
delete_card,
enable_disable_card,
get_card,
+ get_card_by_otp,
get_card_by_uid,
get_cards,
get_hits,
get_refunds,
update_card,
+ update_card_otp,
)
from .models import CreateCardData
@@ -111,6 +113,22 @@ async def enable_card(
return card.dict()
+@boltcards_ext.post("/api/v1/disablecard")
+async def disble_card_with_otp(a):
+ if len(a) < 16:
+ raise HTTPException(detail="Invalid OTP.", status_code=HTTPStatus.BAD_REQUEST)
+ card = await get_card_by_otp(a, half=True)
+ if not card:
+ raise HTTPException(detail="No card found.", status_code=HTTPStatus.NOT_FOUND)
+
+ new_otp = secrets.token_hex(16)
+ await update_card_otp(new_otp, card.id)
+
+ card = await enable_disable_card(enable=False, id=card.id)
+
+ return {"status": "OK"}
+
+
@boltcards_ext.delete("/api/v1/cards/{card_id}")
async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
card = await get_card(card_id)
From 4303af4f1456040f922c6283f1415052a8308ab5 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 29 Sep 2022 20:42:13 +0200
Subject: [PATCH 0013/1058] adjust versions
---
lnbits/extensions/cashu/__init__.py | 5 +-
lnbits/extensions/cashu/config.json | 8 +-
poetry.lock | 1305 ++++++++++++++++++---------
pyproject.toml | 56 +-
4 files changed, 901 insertions(+), 473 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index fa549ad2..d1a1d09c 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -6,9 +6,12 @@ from lnbits.db import Database
from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
+from cashu.mint.router import router as cashu_router
+
db = Database("ext_cashu")
-cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["TPoS"])
+cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
+cashu_ext.include_router(router=cashu_router)
def cashu_renderer():
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index c688b22c..d242a3a7 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -1,6 +1,6 @@
{
- "name": "Cashu Ecash",
- "short_description": "Ecash mints with LN peg in/out",
- "icon": "approval",
- "contributors": ["shinobi", "arcbtc", "calle"]
+ "name": "Cashu Mint",
+ "short_description": "Chaumian Ecash mint",
+ "icon": "donut_small_rounded",
+ "contributors": ["calle"]
}
diff --git a/poetry.lock b/poetry.lock
index 975c62b2..ac75c80a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,45 +20,56 @@ sniffio = ">=1.1"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
-test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
+doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16)"]
[[package]]
name = "asgiref"
-version = "3.4.1"
+version = "3.5.2"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
-name = "atomicwrites"
-version = "1.4.1"
-description = "Atomic file writes."
-category = "dev"
+name = "asn1crypto"
+version = "1.5.1"
+description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
+category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = "*"
[[package]]
name = "attrs"
-version = "21.2.0"
+version = "22.1.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.5"
[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
-docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
+docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
+tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+
+[[package]]
+name = "base58"
+version = "2.1.1"
+description = "Base58 and Base58Check implementation."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"]
[[package]]
name = "bech32"
@@ -78,7 +89,7 @@ python-versions = "*"
[[package]]
name = "black"
-version = "22.6.0"
+version = "22.8.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
@@ -100,24 +111,84 @@ jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
-name = "cerberus"
+name = "cashu"
+version = "0.1.11"
+description = "Ecash wallet and mint with Bitcoin Lightning support"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+anyio = {version = "3.6.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+importlib-metadata = {version = "4.12.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+psycopg2-binary = {version = "2.9.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+py = {version = "1.11.0", 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\""}
+pyparsing = {version = "3.0.9", 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\""}
+python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+typing-extensions = {version = "4.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
+uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+zipp = {version = "3.8.1", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+
+[package.source]
+type = "file"
+url = "../cashu/dist/cashu-0.1.11-py3-none-any.whl"
+
+[[package]]
+name = "Cerberus"
version = "1.3.4"
description = "Lightweight, extensible schema and data validation tool for Python dictionaries."
category = "main"
optional = false
python-versions = ">=2.7"
+[package.dependencies]
+setuptools = "*"
+
[[package]]
name = "certifi"
-version = "2021.5.30"
+version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.6"
[[package]]
name = "cffi"
-version = "1.15.0"
+version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
@@ -128,7 +199,7 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
-version = "2.0.6"
+version = "2.0.12"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
@@ -139,7 +210,7 @@ unicode_backport = ["unicodedata2"]
[[package]]
name = "click"
-version = "8.0.1"
+version = "8.0.4"
description = "Composable command line interface toolkit"
category = "main"
optional = false
@@ -149,6 +220,18 @@ python-versions = ">=3.6"
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+[[package]]
+name = "coincurve"
+version = "17.0.0"
+description = "Cross-platform Python CFFI bindings for libsecp256k1"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+asn1crypto = "*"
+cffi = ">=1.3.0"
+
[[package]]
name = "colorama"
version = "0.4.5"
@@ -159,7 +242,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
-version = "6.4.2"
+version = "6.4.4"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -171,9 +254,28 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
+[[package]]
+name = "cryptography"
+version = "36.0.2"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cffi = ">=1.12"
+
+[package.extras]
+docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
+sdist = ["setuptools_rust (>=0.11.4)"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
+
[[package]]
name = "ecdsa"
-version = "0.17.0"
+version = "0.18.0"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
@@ -194,9 +296,17 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "enum34"
+version = "1.1.10"
+description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4"
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "environs"
-version = "9.3.3"
+version = "9.5.0"
description = "simplified environment variable parsing"
category = "main"
optional = false
@@ -207,14 +317,14 @@ marshmallow = ">=3.0.0"
python-dotenv = "*"
[package.extras]
-dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "tox"]
+dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
-tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"]
+lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
[[package]]
name = "fastapi"
-version = "0.78.0"
+version = "0.83.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
@@ -225,10 +335,24 @@ pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.
starlette = "0.19.1"
[package.extras]
-all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
-dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"]
-doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"]
-test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==22.3.0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"]
+all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
+test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
+
+[[package]]
+name = "grpcio"
+version = "1.49.1"
+description = "HTTP/2-based RPC framework"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+six = ">=1.5.2"
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.49.1)"]
[[package]]
name = "h11"
@@ -282,14 +406,14 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
sniffio = "*"
[package.extras]
-brotli = ["brotlicffi", "brotli"]
-cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "idna"
-version = "3.2"
+version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
@@ -297,26 +421,26 @@ python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
-version = "4.8.1"
+version = "4.12.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"]
perf = ["ipython"]
-testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
-category = "dev"
+category = "main"
optional = false
python-versions = "*"
@@ -329,14 +453,14 @@ optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
-pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
-requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
+pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
plugins = ["setuptools"]
+requirements_deprecated_finder = ["pip-api", "pipreqs"]
[[package]]
-name = "jinja2"
-version = "3.0.1"
+name = "Jinja2"
+version = "3.0.3"
description = "A very fast and expressive template engine."
category = "main"
optional = false
@@ -363,7 +487,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "loguru"
-version = "0.5.3"
+version = "0.6.0"
description = "Python logging made (stupidly) simple"
category = "main"
optional = false
@@ -374,19 +498,19 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
-dev = ["isort (>=5.1.1)", "black (>=19.10b0)", "sphinx-rtd-theme (>=0.4.3)", "sphinx-autobuild (>=0.7.1)", "Sphinx (>=2.2.1)", "pytest-cov (>=2.7.1)", "pytest (>=4.6.2)", "tox-travis (>=0.12)", "tox (>=3.9.0)", "flake8 (>=3.7.7)", "colorama (>=0.3.4)", "codecov (>=2.0.15)"]
+dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
[[package]]
-name = "markupsafe"
-version = "2.0.1"
+name = "MarkupSafe"
+version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[[package]]
name = "marshmallow"
-version = "3.17.0"
+version = "3.18.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "main"
optional = false
@@ -396,9 +520,9 @@ python-versions = ">=3.7"
packaging = ">=17.0"
[package.extras]
-dev = ["pytest", "pytz", "simplejson", "mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)", "tox"]
-docs = ["sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.8)"]
-lint = ["mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)"]
+dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
+docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
+lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -410,7 +534,7 @@ optional = false
python-versions = ">=3.6"
[package.extras]
-build = ["twine", "wheel", "blurb"]
+build = ["blurb", "twine", "wheel"]
docs = ["sphinx"]
test = ["pytest (<5.4)", "pytest-cov"]
@@ -443,11 +567,11 @@ python-versions = "*"
[[package]]
name = "outcome"
-version = "1.1.0"
+version = "1.2.0"
description = "Capture the outcome of Python function calls."
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
attrs = ">=19.2.0"
@@ -463,13 +587,24 @@ python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
+[[package]]
+name = "pathlib2"
+version = "2.3.7.post1"
+description = "Object-oriented filesystem paths"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
[[package]]
name = "pathspec"
-version = "0.9.0"
+version = "0.10.1"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+python-versions = ">=3.7"
[[package]]
name = "platformdirs"
@@ -480,14 +615,14 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
-test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
+docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
+test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.6"
@@ -495,12 +630,20 @@ python-versions = ">=3.6"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
[package.extras]
-testing = ["pytest-benchmark", "pytest"]
-dev = ["tox", "pre-commit"]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "protobuf"
+version = "4.21.6"
+description = ""
+category = "main"
+optional = false
+python-versions = ">=3.7"
[[package]]
name = "psycopg2-binary"
-version = "2.9.1"
+version = "2.9.3"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
@@ -510,7 +653,7 @@ python-versions = ">=3.6"
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -532,19 +675,54 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pydantic"
-version = "1.8.2"
-description = "Data validation and settings management using python 3.6 type hinting"
+version = "1.10.2"
+description = "Data validation and settings management using python type hints"
category = "main"
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.7"
[package.dependencies]
-typing-extensions = ">=3.7.4.3"
+typing-extensions = ">=4.1.0"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
+[[package]]
+name = "pyln-bolt7"
+version = "1.0.246"
+description = "BOLT7"
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[[package]]
+name = "pyln-client"
+version = "0.12.1"
+description = "Client library and plugin library for Core Lightning"
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+pyln-bolt7 = ">=1.0"
+pyln-proto = ">=0.12"
+
+[[package]]
+name = "pyln-proto"
+version = "0.12.0"
+description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+base58 = ">=2.1.1,<3.0.0"
+bitstring = ">=3.1.9,<4.0.0"
+coincurve = ">=17.0.0,<18.0.0"
+cryptography = ">=36.0.1,<37.0.0"
+PySocks = ">=1.7.1,<2.0.0"
+
[[package]]
name = "pyparsing"
version = "3.0.9"
@@ -554,7 +732,7 @@ optional = false
python-versions = ">=3.6.8"
[package.extras]
-diagrams = ["railroad-diagrams", "jinja2"]
+diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pypng"
@@ -565,7 +743,7 @@ optional = false
python-versions = "*"
[[package]]
-name = "pyqrcode"
+name = "PyQRCode"
version = "1.2.1"
description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output."
category = "main"
@@ -576,26 +754,35 @@ python-versions = "*"
PNG = ["pypng (>=0.0.13)"]
[[package]]
-name = "pyscss"
-version = "1.3.7"
+name = "pyScss"
+version = "1.4.0"
description = "pyScss, a Scss compiler for Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
+enum34 = "*"
+pathlib2 = "*"
six = "*"
+[[package]]
+name = "PySocks"
+version = "1.7.1"
+description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
[[package]]
name = "pytest"
-version = "7.1.2"
+version = "7.1.3"
description = "pytest: simple powerful testing with Python"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
-atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
@@ -612,7 +799,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
name = "pytest-asyncio"
version = "0.19.0"
description = "Pytest support for asyncio"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -621,7 +808,7 @@ pytest = ">=6.1.0"
typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""}
[package.extras]
-testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
+testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
name = "pytest-cov"
@@ -636,21 +823,21 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
-testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "python-dotenv"
-version = "0.19.0"
+version = "0.21.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
-name = "pyyaml"
+name = "PyYAML"
version = "5.4.1"
description = "YAML parser and emitter for Python"
category = "main"
@@ -658,7 +845,7 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
-name = "represent"
+name = "Represent"
version = "1.6.0.post0"
description = "Create __repr__ automatically or declaratively."
category = "main"
@@ -669,7 +856,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
six = ">=1.8.0"
[package.extras]
-test = ["ipython", "pytest (>=3.0.5)", "mock"]
+test = ["ipython", "mock", "pytest (>=3.0.5)"]
+
+[[package]]
+name = "requests"
+version = "2.27.1"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]]
name = "rfc3986"
@@ -696,6 +901,19 @@ python-versions = "*"
[package.dependencies]
cffi = ">=1.3.0"
+[[package]]
+name = "setuptools"
+version = "65.4.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
[[package]]
name = "shortuuid"
version = "1.0.1"
@@ -714,15 +932,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sniffio"
-version = "1.2.0"
+version = "1.3.0"
description = "Sniff out which async library your code is running under"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
[[package]]
-name = "sqlalchemy"
-version = "1.3.23"
+name = "SQLAlchemy"
+version = "1.3.24"
description = "Database Abstraction Library"
category = "main"
optional = false
@@ -733,12 +951,12 @@ mssql = ["pyodbc"]
mssql_pymssql = ["pymssql"]
mssql_pyodbc = ["pyodbc"]
mysql = ["mysqlclient"]
-oracle = ["cx-oracle"]
+oracle = ["cx_oracle"]
postgresql = ["psycopg2"]
postgresql_pg8000 = ["pg8000 (<1.16.6)"]
postgresql_psycopg2binary = ["psycopg2-binary"]
postgresql_psycopg2cffi = ["psycopg2cffi"]
-pymysql = ["pymysql (<1)", "pymysql"]
+pymysql = ["pymysql", "pymysql (<1)"]
[[package]]
name = "sqlalchemy-aio"
@@ -785,7 +1003,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -799,7 +1017,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-protobuf"
-version = "3.19.22"
+version = "3.20.4"
description = "Typing stubs for protobuf"
category = "dev"
optional = false
@@ -807,15 +1025,28 @@ python-versions = "*"
[[package]]
name = "typing-extensions"
-version = "3.10.0.2"
-description = "Backported and Experimental Type Hints for Python 3.5+"
+version = "4.3.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
+
+[[package]]
+name = "urllib3"
+version = "1.26.12"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "uvicorn"
-version = "0.18.1"
+version = "0.18.3"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@@ -827,7 +1058,7 @@ h11 = ">=0.8"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchfiles (>=0.13)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
+standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
[[package]]
name = "uvloop"
@@ -838,9 +1069,9 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
-docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
-test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"]
[[package]]
name = "watchgod"
@@ -884,20 +1115,20 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "zipp"
-version = "3.5.0"
+version = "3.8.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
+testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9 | ^3.8 | ^3.7"
-content-hash = "cadb8f2e46f0c083e91956f4f0f70b53b6c106f1c0b47972b57132dfee357367"
+content-hash = "0b5a58944599b4218ce2f74e1d101002d7b97072b9069d0e32772fb23d35da69"
[metadata.files]
aiofiles = [
@@ -909,15 +1140,20 @@ anyio = [
{file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
]
asgiref = [
- {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
- {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
+ {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"},
+ {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"},
]
-atomicwrites = [
- {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
+asn1crypto = [
+ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
+ {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
attrs = [
- {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
- {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
+ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+]
+base58 = [
+ {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"},
+ {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"},
]
bech32 = [
{file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"},
@@ -929,158 +1165,294 @@ bitstring = [
{file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
]
black = [
- {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
- {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
- {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"},
- {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"},
- {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"},
- {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"},
- {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"},
- {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"},
- {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"},
- {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"},
- {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"},
- {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"},
- {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"},
- {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"},
- {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"},
- {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"},
- {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"},
- {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"},
- {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"},
- {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"},
- {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"},
- {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"},
- {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"},
+ {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
+ {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
+ {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
+ {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
+ {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
+ {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
+ {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
+ {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
+ {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
+ {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
+ {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
+ {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
+ {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
+ {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
+ {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
+ {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
+ {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
+ {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
+ {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
+ {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
+ {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
+ {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
+ {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
]
-cerberus = [
+cashu = [
+ {file = "cashu-0.1.11-py3-none-any.whl", hash = "sha256:d2c5a72648fa4487fdf694e5669dfaa8d62445d83cced33b5bc63eccbce6a00c"},
+]
+Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
certifi = [
- {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
- {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
+ {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
+ {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
cffi = [
- {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
- {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
- {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
- {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
- {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
- {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
- {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
- {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
- {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
- {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
- {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
- {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
- {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
- {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
- {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
- {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
- {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
- {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
- {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
- {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
- {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
- {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
- {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
- {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
- {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+ {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+ {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+ {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+ {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+ {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+ {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+ {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+ {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+ {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+ {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+ {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+ {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+ {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+ {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+ {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+ {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
charset-normalizer = [
- {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
- {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
+ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
+ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
]
click = [
- {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
- {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+ {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
+ {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
+]
+coincurve = [
+ {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"},
+ {file = "coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"},
+ {file = "coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"},
+ {file = "coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"},
+ {file = "coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"},
+ {file = "coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"},
+ {file = "coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"},
+ {file = "coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"},
+ {file = "coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"},
+ {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"},
]
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
coverage = [
- {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"},
- {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"},
- {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"},
- {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"},
- {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"},
- {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"},
- {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"},
- {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"},
- {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"},
- {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"},
- {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"},
- {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"},
- {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"},
- {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"},
- {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"},
- {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"},
- {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"},
- {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"},
- {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"},
- {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"},
- {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"},
- {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"},
- {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"},
- {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"},
- {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"},
- {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"},
- {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"},
- {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"},
- {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"},
- {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"},
- {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"},
- {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"},
- {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"},
- {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"},
- {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"},
- {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"},
- {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"},
- {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"},
- {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"},
- {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"},
- {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"},
+ {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"},
+ {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"},
+ {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"},
+ {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"},
+ {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"},
+ {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"},
+ {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"},
+ {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"},
+ {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"},
+ {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"},
+ {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"},
+ {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"},
+ {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"},
+ {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"},
+ {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"},
+ {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"},
+ {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"},
+ {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"},
+ {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"},
+ {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"},
+ {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"},
+ {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"},
+ {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"},
+ {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"},
+ {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"},
+ {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"},
+ {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"},
+ {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"},
+ {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"},
+ {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"},
+ {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"},
+ {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"},
+ {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"},
+ {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"},
+ {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"},
+ {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"},
+ {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"},
+ {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"},
+ {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"},
+ {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"},
+ {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"},
+ {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"},
+ {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"},
+ {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"},
+ {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"},
+ {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"},
+ {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"},
+ {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"},
+ {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
+ {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
+]
+cryptography = [
+ {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
+ {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"},
+ {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"},
+ {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"},
+ {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"},
+ {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"},
+ {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
]
ecdsa = [
- {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
- {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
+ {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
+ {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
]
embit = [
{file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"},
]
+enum34 = [
+ {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"},
+ {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"},
+ {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"},
+]
environs = [
- {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
- {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
+ {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
+ {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
]
fastapi = [
- {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
- {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
+ {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"},
+ {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
+]
+grpcio = [
+ {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
+ {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"},
+ {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"},
+ {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"},
+ {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"},
+ {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"},
+ {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"},
+ {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"},
+ {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"},
+ {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"},
+ {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"},
+ {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"},
+ {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"},
+ {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"},
+ {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"},
+ {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"},
+ {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"},
+ {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"},
+ {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"},
+ {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"},
+ {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"},
+ {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"},
+ {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"},
+ {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"},
+ {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"},
+ {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"},
+ {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"},
+ {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"},
+ {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"},
+ {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"},
+ {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"},
+ {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"},
+ {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -1131,12 +1503,12 @@ httpx = [
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
]
idna = [
- {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
- {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
- {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
+ {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1146,92 +1518,63 @@ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
-jinja2 = [
- {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
- {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
+Jinja2 = [
+ {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
+ {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
]
lnurl = [
{file = "lnurl-0.3.6-py3-none-any.whl", hash = "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92"},
{file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"},
]
loguru = [
- {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
- {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
+ {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
+ {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
]
-markupsafe = [
- {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
- {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+MarkupSafe = [
+ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
+ {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
+ {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
+ {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
+ {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
+ {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
]
marshmallow = [
- {file = "marshmallow-3.17.0-py3-none-any.whl", hash = "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb"},
- {file = "marshmallow-3.17.0.tar.gz", hash = "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"},
+ {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"},
+ {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"},
]
mock = [
{file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"},
@@ -1267,16 +1610,20 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
outcome = [
- {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
- {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
+ {file = "outcome-1.2.0-py2.py3-none-any.whl", hash = "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"},
+ {file = "outcome-1.2.0.tar.gz", hash = "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
+pathlib2 = [
+ {file = "pathlib2-2.3.7.post1-py2.py3-none-any.whl", hash = "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b"},
+ {file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"},
+]
pathspec = [
- {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
- {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
+ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
+ {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
@@ -1286,43 +1633,79 @@ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
+protobuf = [
+ {file = "protobuf-4.21.6-cp310-abi3-win32.whl", hash = "sha256:49f88d56a9180dbb7f6199c920f5bb5c1dd0172f672983bb281298d57c2ac8eb"},
+ {file = "protobuf-4.21.6-cp310-abi3-win_amd64.whl", hash = "sha256:7a6cc8842257265bdfd6b74d088b829e44bcac3cca234c5fdd6052730017b9ea"},
+ {file = "protobuf-4.21.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ba596b9ffb85c909fcfe1b1a23136224ed678af3faf9912d3fa483d5f9813c4e"},
+ {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4143513c766db85b9d7c18dbf8339673c8a290131b2a0fe73855ab20770f72b0"},
+ {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6cea204865595a92a7b240e4b65bcaaca3ad5d2ce25d9db3756eba06041138e"},
+ {file = "protobuf-4.21.6-cp37-cp37m-win32.whl", hash = "sha256:9666da97129138585b26afcb63ad4887f602e169cafe754a8258541c553b8b5d"},
+ {file = "protobuf-4.21.6-cp37-cp37m-win_amd64.whl", hash = "sha256:308173d3e5a3528787bb8c93abea81d5a950bdce62840d9760effc84127fb39c"},
+ {file = "protobuf-4.21.6-cp38-cp38-win32.whl", hash = "sha256:aa29113ec901281f29d9d27b01193407a98aa9658b8a777b0325e6d97149f5ce"},
+ {file = "protobuf-4.21.6-cp38-cp38-win_amd64.whl", hash = "sha256:8f9e60f7d44592c66e7b332b6a7b4b6e8d8b889393c79dbc3a91f815118f8eac"},
+ {file = "protobuf-4.21.6-cp39-cp39-win32.whl", hash = "sha256:80e6540381080715fddac12690ee42d087d0d17395f8d0078dfd6f1181e7be4c"},
+ {file = "protobuf-4.21.6-cp39-cp39-win_amd64.whl", hash = "sha256:77b355c8604fe285536155286b28b0c4cbc57cf81b08d8357bf34829ea982860"},
+ {file = "protobuf-4.21.6-py2.py3-none-any.whl", hash = "sha256:07a0bb9cc6114f16a39c866dc28b6e3d96fa4ffb9cc1033057412547e6e75cb9"},
+ {file = "protobuf-4.21.6-py3-none-any.whl", hash = "sha256:c7c864148a237f058c739ae7a05a2b403c0dfa4ce7d1f3e5213f352ad52d57c6"},
+ {file = "protobuf-4.21.6.tar.gz", hash = "sha256:6b1040a5661cd5f6e610cbca9cfaa2a17d60e2bb545309bc1b278bb05be44bdd"},
+]
psycopg2-binary = [
- {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-win32.whl", hash = "sha256:ebccf1123e7ef66efc615a68295bf6fdba875a75d5bba10a05073202598085fc"},
- {file = "psycopg2_binary-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:1f6ca4a9068f5c5c57e744b4baa79f40e83e3746875cac3c45467b16326bab45"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"},
- {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"},
- {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"},
- {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"},
- {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"},
+ {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
@@ -1362,28 +1745,54 @@ pycryptodomex = [
{file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
]
pydantic = [
- {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
- {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
- {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
- {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
- {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
- {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
- {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
- {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
- {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
- {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+ {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
+ {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
+ {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
+ {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
+ {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
+ {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
+ {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
+ {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
+ {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
+ {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
+ {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
+ {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
+ {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
+ {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
+ {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
+ {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
+ {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
+ {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
+ {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
+ {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
+ {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
+ {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
+ {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
+ {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
+ {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
+ {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
+ {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
+ {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
+ {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
+ {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
+ {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
+ {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
+ {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
+ {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
+ {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
+ {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
+]
+pyln-bolt7 = [
+ {file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"},
+ {file = "pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5"},
+]
+pyln-client = [
+ {file = "pyln-client-0.12.1.tar.gz", hash = "sha256:f14fa7947b65ecde2753984452441cf41b7b25b1a0ba7beced48786fa54d2bfe"},
+ {file = "pyln_client-0.12.1-py3-none-any.whl", hash = "sha256:6b500bcc49e4028d50692b962d9c9f7e9ede920d718f9b9412f04f7db0aa0e63"},
+]
+pyln-proto = [
+ {file = "pyln-proto-0.12.0.tar.gz", hash = "sha256:3214d99d8385f2135a94937f0dc1da626a33b257e9ebc320841656edaefabbe5"},
+ {file = "pyln_proto-0.12.0-py3-none-any.whl", hash = "sha256:dedef5d8e476a9ade5a0b2eb919ccc37e4a57f2a78fdc399f1c5e0de17e41604"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
@@ -1392,16 +1801,21 @@ pyparsing = [
pypng = [
{file = "pypng-0.0.21-py3-none-any.whl", hash = "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"},
]
-pyqrcode = [
+PyQRCode = [
{file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"},
{file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"},
]
-pyscss = [
- {file = "pyScss-1.3.7.tar.gz", hash = "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"},
+pyScss = [
+ {file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"},
+]
+PySocks = [
+ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
+ {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
+ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
]
pytest = [
- {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
- {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
+ {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
+ {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
]
pytest-asyncio = [
{file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"},
@@ -1412,10 +1826,10 @@ pytest-cov = [
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
python-dotenv = [
- {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
- {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+ {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
+ {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
]
-pyyaml = [
+PyYAML = [
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
{file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
{file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
@@ -1446,10 +1860,14 @@ pyyaml = [
{file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
-represent = [
+Represent = [
{file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"},
{file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"},
]
+requests = [
+ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+]
rfc3986 = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
@@ -1479,6 +1897,10 @@ secp256k1 = [
{file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4"},
{file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
]
+setuptools = [
+ {file = "setuptools-65.4.0-py3-none-any.whl", hash = "sha256:c2d2709550f15aab6c9110196ea312f468f41cd546bceb24127a1be6fdcaeeb1"},
+ {file = "setuptools-65.4.0.tar.gz", hash = "sha256:a8f6e213b4b0661f590ccf40de95d28a177cd747d098624ad3f69c40287297e9"},
+]
shortuuid = [
{file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
{file = "shortuuid-1.0.1.tar.gz", hash = "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f"},
@@ -1488,48 +1910,44 @@ six = [
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sniffio = [
- {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
- {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
+ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
]
-sqlalchemy = [
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"},
- {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"},
+SQLAlchemy = [
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"},
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"},
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-win32.whl", hash = "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-win_amd64.whl", hash = "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-win32.whl", hash = "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-win_amd64.whl", hash = "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-win32.whl", hash = "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-win_amd64.whl", hash = "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-win32.whl", hash = "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-win_amd64.whl", hash = "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-win32.whl", hash = "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"},
+ {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"},
]
sqlalchemy-aio = [
{file = "sqlalchemy_aio-0.17.0-py3-none-any.whl", hash = "sha256:3f4aa392c38f032d6734826a4138a0f02ed3122d442ed142be1e5964f2a33b60"},
@@ -1573,17 +1991,20 @@ typed-ast = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
]
types-protobuf = [
- {file = "types-protobuf-3.19.22.tar.gz", hash = "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"},
- {file = "types_protobuf-3.19.22-py3-none-any.whl", hash = "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab"},
+ {file = "types-protobuf-3.20.4.tar.gz", hash = "sha256:0dad3a5009895c985a56e2837f61902bad9594151265ac0ee907bb16d0b01eb7"},
+ {file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"},
]
typing-extensions = [
- {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
- {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
- {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+ {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
+ {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
+ {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
uvicorn = [
- {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"},
- {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"},
+ {file = "uvicorn-0.18.3-py3-none-any.whl", hash = "sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af"},
+ {file = "uvicorn-0.18.3.tar.gz", hash = "sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
@@ -1643,6 +2064,6 @@ win32-setctime = [
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
zipp = [
- {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
- {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+ {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"},
+ {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"},
]
diff --git a/pyproject.toml b/pyproject.toml
index 1ae8c1fe..f106350c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,55 +11,59 @@ script = "build.py"
[tool.poetry.dependencies]
python = "^3.9 | ^3.8 | ^3.7"
aiofiles = "0.8.0"
-asgiref = "3.4.1"
-attrs = "21.2.0"
+asgiref = "^3.4.1"
+attrs = "22.1.0"
bech32 = "1.2.0"
bitstring = "3.1.9"
cerberus = "1.3.4"
-certifi = "2021.5.30"
-charset-normalizer = "2.0.6"
-click = "8.0.1"
-ecdsa = "0.17.0"
+certifi = "2022.9.24"
+charset-normalizer = "2.0.12"
+click = "8.0.4"
+ecdsa = "0.18.0"
embit = "0.4.9"
-environs = "9.3.3"
-fastapi = "0.78.0"
+environs = "9.5.0"
+fastapi = "0.83.0"
h11 = "0.12.0"
-httpcore = "0.15.0"
httptools = "0.4.0"
httpx = "0.23.0"
-idna = "3.2"
-importlib-metadata = "4.8.1"
-jinja2 = "3.0.1"
+idna = "3.4"
+importlib-metadata = "^4.8.1"
+jinja2 = "3.0.3"
lnurl = "0.3.6"
-markupsafe = "2.0.1"
-marshmallow = "3.17.0"
-outcome = "1.1.0"
-psycopg2-binary = "2.9.1"
+markupsafe = "2.1.1"
+marshmallow = "3.18.0"
+outcome = "1.2.0"
+psycopg2-binary = "2.9.3"
pycryptodomex = "3.14.1"
-pydantic = "1.8.2"
+pydantic = "1.10.2"
pypng = "0.0.21"
pyqrcode = "1.2.1"
-pyscss = "1.3.7"
-python-dotenv = "0.19.0"
+pyScss = "1.4.0"
+python-dotenv = "0.21.0"
pyyaml = "5.4.1"
represent = "1.6.0.post0"
rfc3986 = "1.5.0"
secp256k1 = "0.14.0"
shortuuid = "1.0.1"
six = "1.16.0"
-sniffio = "1.2.0"
-sqlalchemy = "1.3.23"
+sniffio = "1.3.0"
+sqlalchemy = "1.3.24"
sqlalchemy-aio = "0.17.0"
sse-starlette = "0.6.2"
-typing-extensions = "3.10.0.2"
-uvicorn = "0.18.1"
+typing-extensions = "4.3.0"
+uvicorn = "0.18.3"
uvloop = "0.16.0"
watchgod = "0.7"
websockets = "10.0"
-zipp = "3.5.0"
-loguru = "0.5.3"
-cffi = "1.15.0"
+zipp = "^3.5.0"
+loguru = "0.6.0"
+cffi = "1.15.1"
websocket-client = "1.3.3"
+grpcio = "^1.49.1"
+protobuf = "^4.21.6"
+pyln-client = "^0.12.0"
+httpcore = "0.15.0"
+cashu = {path = "../cashu/dist/cashu-0.1.11-py3-none-any.whl"}
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From 1e61a05e202c8eed6271d1af3c3007e7e1a49350 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 29 Sep 2022 20:42:13 +0200
Subject: [PATCH 0014/1058] Revert "adjust versions"
This reverts commit 4303af4f1456040f922c6283f1415052a8308ab5.
---
lnbits/extensions/cashu/__init__.py | 5 +-
lnbits/extensions/cashu/config.json | 8 +-
poetry.lock | 1305 +++++++++------------------
pyproject.toml | 56 +-
4 files changed, 473 insertions(+), 901 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index d1a1d09c..fa549ad2 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -6,12 +6,9 @@ from lnbits.db import Database
from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
-from cashu.mint.router import router as cashu_router
-
db = Database("ext_cashu")
-cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
-cashu_ext.include_router(router=cashu_router)
+cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["TPoS"])
def cashu_renderer():
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index d242a3a7..c688b22c 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -1,6 +1,6 @@
{
- "name": "Cashu Mint",
- "short_description": "Chaumian Ecash mint",
- "icon": "donut_small_rounded",
- "contributors": ["calle"]
+ "name": "Cashu Ecash",
+ "short_description": "Ecash mints with LN peg in/out",
+ "icon": "approval",
+ "contributors": ["shinobi", "arcbtc", "calle"]
}
diff --git a/poetry.lock b/poetry.lock
index ac75c80a..975c62b2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,56 +20,45 @@ sniffio = ">=1.1"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
-test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
+doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
+test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16)"]
[[package]]
name = "asgiref"
-version = "3.5.2"
+version = "3.4.1"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6"
[package.dependencies]
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
+tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
[[package]]
-name = "asn1crypto"
-version = "1.5.1"
-description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
-category = "main"
+name = "atomicwrites"
+version = "1.4.1"
+description = "Atomic file writes."
+category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
-version = "22.1.0"
+version = "21.2.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
-docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
-
-[[package]]
-name = "base58"
-version = "2.1.1"
-description = "Base58 and Base58Check implementation."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
[[package]]
name = "bech32"
@@ -89,7 +78,7 @@ python-versions = "*"
[[package]]
name = "black"
-version = "22.8.0"
+version = "22.6.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
@@ -111,84 +100,24 @@ jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
-name = "cashu"
-version = "0.1.11"
-description = "Ecash wallet and mint with Bitcoin Lightning support"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-anyio = {version = "3.6.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
-ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-importlib-metadata = {version = "4.12.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
-iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-psycopg2-binary = {version = "2.9.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-py = {version = "1.11.0", 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\""}
-pyparsing = {version = "3.0.9", 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\""}
-python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-typing-extensions = {version = "4.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
-uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
-zipp = {version = "3.8.1", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
-
-[package.source]
-type = "file"
-url = "../cashu/dist/cashu-0.1.11-py3-none-any.whl"
-
-[[package]]
-name = "Cerberus"
+name = "cerberus"
version = "1.3.4"
description = "Lightweight, extensible schema and data validation tool for Python dictionaries."
category = "main"
optional = false
python-versions = ">=2.7"
-[package.dependencies]
-setuptools = "*"
-
[[package]]
name = "certifi"
-version = "2022.9.24"
+version = "2021.5.30"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = "*"
[[package]]
name = "cffi"
-version = "1.15.1"
+version = "1.15.0"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
@@ -199,7 +128,7 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
-version = "2.0.12"
+version = "2.0.6"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
@@ -210,7 +139,7 @@ unicode_backport = ["unicodedata2"]
[[package]]
name = "click"
-version = "8.0.4"
+version = "8.0.1"
description = "Composable command line interface toolkit"
category = "main"
optional = false
@@ -220,18 +149,6 @@ python-versions = ">=3.6"
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-[[package]]
-name = "coincurve"
-version = "17.0.0"
-description = "Cross-platform Python CFFI bindings for libsecp256k1"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-asn1crypto = "*"
-cffi = ">=1.3.0"
-
[[package]]
name = "colorama"
version = "0.4.5"
@@ -242,7 +159,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
-version = "6.4.4"
+version = "6.4.2"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -254,28 +171,9 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
-[[package]]
-name = "cryptography"
-version = "36.0.2"
-description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-cffi = ">=1.12"
-
-[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"]
-docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
-pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
-sdist = ["setuptools_rust (>=0.11.4)"]
-ssh = ["bcrypt (>=3.1.5)"]
-test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
-
[[package]]
name = "ecdsa"
-version = "0.18.0"
+version = "0.17.0"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
@@ -296,17 +194,9 @@ category = "main"
optional = false
python-versions = "*"
-[[package]]
-name = "enum34"
-version = "1.1.10"
-description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4"
-category = "main"
-optional = false
-python-versions = "*"
-
[[package]]
name = "environs"
-version = "9.5.0"
+version = "9.3.3"
description = "simplified environment variable parsing"
category = "main"
optional = false
@@ -317,14 +207,14 @@ marshmallow = ">=3.0.0"
python-dotenv = "*"
[package.extras]
-dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
+dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "tox"]
django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
-tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
+lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"]
[[package]]
name = "fastapi"
-version = "0.83.0"
+version = "0.78.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
@@ -335,24 +225,10 @@ pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.
starlette = "0.19.1"
[package.extras]
-all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
-dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
-doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
-test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
-
-[[package]]
-name = "grpcio"
-version = "1.49.1"
-description = "HTTP/2-based RPC framework"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-six = ">=1.5.2"
-
-[package.extras]
-protobuf = ["grpcio-tools (>=1.49.1)"]
+all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==22.3.0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"]
[[package]]
name = "h11"
@@ -406,14 +282,14 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
sniffio = "*"
[package.extras]
-brotli = ["brotli", "brotlicffi"]
-cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
+brotli = ["brotlicffi", "brotli"]
+cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "idna"
-version = "3.4"
+version = "3.2"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
@@ -421,26 +297,26 @@ python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
-version = "4.12.0"
+version = "4.8.1"
description = "Read metadata from Python packages"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
@@ -453,14 +329,14 @@ optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
-colors = ["colorama (>=0.4.3,<0.5.0)"]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
+requirements_deprecated_finder = ["pipreqs", "pip-api"]
+colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
-requirements_deprecated_finder = ["pip-api", "pipreqs"]
[[package]]
-name = "Jinja2"
-version = "3.0.3"
+name = "jinja2"
+version = "3.0.1"
description = "A very fast and expressive template engine."
category = "main"
optional = false
@@ -487,7 +363,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "loguru"
-version = "0.6.0"
+version = "0.5.3"
description = "Python logging made (stupidly) simple"
category = "main"
optional = false
@@ -498,19 +374,19 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
-dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
+dev = ["isort (>=5.1.1)", "black (>=19.10b0)", "sphinx-rtd-theme (>=0.4.3)", "sphinx-autobuild (>=0.7.1)", "Sphinx (>=2.2.1)", "pytest-cov (>=2.7.1)", "pytest (>=4.6.2)", "tox-travis (>=0.12)", "tox (>=3.9.0)", "flake8 (>=3.7.7)", "colorama (>=0.3.4)", "codecov (>=2.0.15)"]
[[package]]
-name = "MarkupSafe"
-version = "2.1.1"
+name = "markupsafe"
+version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6"
[[package]]
name = "marshmallow"
-version = "3.18.0"
+version = "3.17.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "main"
optional = false
@@ -520,9 +396,9 @@ python-versions = ">=3.7"
packaging = ">=17.0"
[package.extras]
-dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
-docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
-lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"]
+dev = ["pytest", "pytz", "simplejson", "mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)", "tox"]
+docs = ["sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.8)"]
+lint = ["mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -534,7 +410,7 @@ optional = false
python-versions = ">=3.6"
[package.extras]
-build = ["blurb", "twine", "wheel"]
+build = ["twine", "wheel", "blurb"]
docs = ["sphinx"]
test = ["pytest (<5.4)", "pytest-cov"]
@@ -567,11 +443,11 @@ python-versions = "*"
[[package]]
name = "outcome"
-version = "1.2.0"
+version = "1.1.0"
description = "Capture the outcome of Python function calls."
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6"
[package.dependencies]
attrs = ">=19.2.0"
@@ -587,24 +463,13 @@ python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
-[[package]]
-name = "pathlib2"
-version = "2.3.7.post1"
-description = "Object-oriented filesystem paths"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-six = "*"
-
[[package]]
name = "pathspec"
-version = "0.10.1"
+version = "0.9.0"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
-python-versions = ">=3.7"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[[package]]
name = "platformdirs"
@@ -615,14 +480,14 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
-test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
+docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
+test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
@@ -630,20 +495,12 @@ python-versions = ">=3.6"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "protobuf"
-version = "4.21.6"
-description = ""
-category = "main"
-optional = false
-python-versions = ">=3.7"
+testing = ["pytest-benchmark", "pytest"]
+dev = ["tox", "pre-commit"]
[[package]]
name = "psycopg2-binary"
-version = "2.9.3"
+version = "2.9.1"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
@@ -653,7 +510,7 @@ python-versions = ">=3.6"
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -675,54 +532,19 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pydantic"
-version = "1.10.2"
-description = "Data validation and settings management using python type hints"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6.1"
[package.dependencies]
-typing-extensions = ">=4.1.0"
+typing-extensions = ">=3.7.4.3"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
-[[package]]
-name = "pyln-bolt7"
-version = "1.0.246"
-description = "BOLT7"
-category = "main"
-optional = false
-python-versions = ">=3.7,<4.0"
-
-[[package]]
-name = "pyln-client"
-version = "0.12.1"
-description = "Client library and plugin library for Core Lightning"
-category = "main"
-optional = false
-python-versions = ">=3.7,<4.0"
-
-[package.dependencies]
-pyln-bolt7 = ">=1.0"
-pyln-proto = ">=0.12"
-
-[[package]]
-name = "pyln-proto"
-version = "0.12.0"
-description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
-category = "main"
-optional = false
-python-versions = ">=3.7,<4.0"
-
-[package.dependencies]
-base58 = ">=2.1.1,<3.0.0"
-bitstring = ">=3.1.9,<4.0.0"
-coincurve = ">=17.0.0,<18.0.0"
-cryptography = ">=36.0.1,<37.0.0"
-PySocks = ">=1.7.1,<2.0.0"
-
[[package]]
name = "pyparsing"
version = "3.0.9"
@@ -732,7 +554,7 @@ optional = false
python-versions = ">=3.6.8"
[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
+diagrams = ["railroad-diagrams", "jinja2"]
[[package]]
name = "pypng"
@@ -743,7 +565,7 @@ optional = false
python-versions = "*"
[[package]]
-name = "PyQRCode"
+name = "pyqrcode"
version = "1.2.1"
description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output."
category = "main"
@@ -754,35 +576,26 @@ python-versions = "*"
PNG = ["pypng (>=0.0.13)"]
[[package]]
-name = "pyScss"
-version = "1.4.0"
+name = "pyscss"
+version = "1.3.7"
description = "pyScss, a Scss compiler for Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
-enum34 = "*"
-pathlib2 = "*"
six = "*"
-[[package]]
-name = "PySocks"
-version = "1.7.1"
-description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
[[package]]
name = "pytest"
-version = "7.1.3"
+version = "7.1.2"
description = "pytest: simple powerful testing with Python"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
+atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
@@ -799,7 +612,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
name = "pytest-asyncio"
version = "0.19.0"
description = "Pytest support for asyncio"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -808,7 +621,7 @@ pytest = ">=6.1.0"
typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""}
[package.extras]
-testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
+testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
name = "pytest-cov"
@@ -823,21 +636,21 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"]
[[package]]
name = "python-dotenv"
-version = "0.21.0"
+version = "0.19.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.5"
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
-name = "PyYAML"
+name = "pyyaml"
version = "5.4.1"
description = "YAML parser and emitter for Python"
category = "main"
@@ -845,7 +658,7 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
-name = "Represent"
+name = "represent"
version = "1.6.0.post0"
description = "Create __repr__ automatically or declaratively."
category = "main"
@@ -856,25 +669,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
six = ">=1.8.0"
[package.extras]
-test = ["ipython", "mock", "pytest (>=3.0.5)"]
-
-[[package]]
-name = "requests"
-version = "2.27.1"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+test = ["ipython", "pytest (>=3.0.5)", "mock"]
[[package]]
name = "rfc3986"
@@ -901,19 +696,6 @@ python-versions = "*"
[package.dependencies]
cffi = ">=1.3.0"
-[[package]]
-name = "setuptools"
-version = "65.4.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
[[package]]
name = "shortuuid"
version = "1.0.1"
@@ -932,15 +714,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sniffio"
-version = "1.3.0"
+version = "1.2.0"
description = "Sniff out which async library your code is running under"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.5"
[[package]]
-name = "SQLAlchemy"
-version = "1.3.24"
+name = "sqlalchemy"
+version = "1.3.23"
description = "Database Abstraction Library"
category = "main"
optional = false
@@ -951,12 +733,12 @@ mssql = ["pyodbc"]
mssql_pymssql = ["pymssql"]
mssql_pyodbc = ["pyodbc"]
mysql = ["mysqlclient"]
-oracle = ["cx_oracle"]
+oracle = ["cx-oracle"]
postgresql = ["psycopg2"]
postgresql_pg8000 = ["pg8000 (<1.16.6)"]
postgresql_psycopg2binary = ["psycopg2-binary"]
postgresql_psycopg2cffi = ["psycopg2cffi"]
-pymysql = ["pymysql", "pymysql (<1)"]
+pymysql = ["pymysql (<1)", "pymysql"]
[[package]]
name = "sqlalchemy-aio"
@@ -1003,7 +785,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -1017,7 +799,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-protobuf"
-version = "3.20.4"
+version = "3.19.22"
description = "Typing stubs for protobuf"
category = "dev"
optional = false
@@ -1025,28 +807,15 @@ python-versions = "*"
[[package]]
name = "typing-extensions"
-version = "4.3.0"
-description = "Backported and Experimental Type Hints for Python 3.7+"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
category = "main"
optional = false
-python-versions = ">=3.7"
-
-[[package]]
-name = "urllib3"
-version = "1.26.12"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
-
-[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
-secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+python-versions = "*"
[[package]]
name = "uvicorn"
-version = "0.18.3"
+version = "0.18.1"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@@ -1058,7 +827,7 @@ h11 = ">=0.8"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
+standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchfiles (>=0.13)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
[[package]]
name = "uvloop"
@@ -1069,9 +838,9 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
-docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
-test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"]
+dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
[[package]]
name = "watchgod"
@@ -1115,20 +884,20 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "zipp"
-version = "3.8.1"
+version = "3.5.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.6"
[package.extras]
-docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
-testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9 | ^3.8 | ^3.7"
-content-hash = "0b5a58944599b4218ce2f74e1d101002d7b97072b9069d0e32772fb23d35da69"
+content-hash = "cadb8f2e46f0c083e91956f4f0f70b53b6c106f1c0b47972b57132dfee357367"
[metadata.files]
aiofiles = [
@@ -1140,20 +909,15 @@ anyio = [
{file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
]
asgiref = [
- {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"},
- {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"},
+ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
+ {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
]
-asn1crypto = [
- {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
- {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
+atomicwrites = [
+ {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
]
attrs = [
- {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
- {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
-]
-base58 = [
- {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"},
- {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"},
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
]
bech32 = [
{file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"},
@@ -1165,294 +929,158 @@ bitstring = [
{file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
]
black = [
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
- {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
- {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
- {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
- {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
- {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
- {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
- {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
- {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
- {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
- {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
- {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
- {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
- {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
- {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
- {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
- {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
- {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
+ {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
+ {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
+ {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"},
+ {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"},
+ {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"},
+ {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"},
+ {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"},
+ {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"},
+ {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"},
+ {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"},
+ {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"},
+ {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"},
+ {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"},
+ {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"},
+ {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"},
+ {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"},
+ {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"},
+ {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"},
+ {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"},
+ {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"},
+ {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"},
+ {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"},
+ {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"},
]
-cashu = [
- {file = "cashu-0.1.11-py3-none-any.whl", hash = "sha256:d2c5a72648fa4487fdf694e5669dfaa8d62445d83cced33b5bc63eccbce6a00c"},
-]
-Cerberus = [
+cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
certifi = [
- {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
- {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
+ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+ {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
]
cffi = [
- {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
- {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
- {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
- {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
- {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
- {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
- {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
- {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
- {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
- {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
- {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
- {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
- {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
- {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
- {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
- {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
- {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
- {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
- {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
+ {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
+ {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
+ {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
+ {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
+ {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
+ {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
+ {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
+ {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
+ {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
+ {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
+ {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
+ {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
+ {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
+ {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
+ {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
+ {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
]
charset-normalizer = [
- {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
- {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
+ {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
+ {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
]
click = [
- {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
- {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
-]
-coincurve = [
- {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"},
- {file = "coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"},
- {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"},
- {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"},
- {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"},
- {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"},
- {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"},
- {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"},
- {file = "coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"},
- {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"},
- {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"},
- {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"},
- {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"},
- {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"},
- {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"},
- {file = "coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"},
- {file = "coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"},
- {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"},
- {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"},
- {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"},
- {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"},
- {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"},
- {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"},
- {file = "coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"},
- {file = "coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"},
- {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"},
- {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"},
- {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"},
- {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"},
- {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"},
- {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"},
- {file = "coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"},
- {file = "coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"},
- {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"},
+ {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+ {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
]
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
coverage = [
- {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"},
- {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"},
- {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"},
- {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"},
- {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"},
- {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"},
- {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"},
- {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"},
- {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"},
- {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"},
- {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"},
- {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"},
- {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"},
- {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"},
- {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"},
- {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"},
- {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"},
- {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"},
- {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
- {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
-]
-cryptography = [
- {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
- {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"},
- {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"},
- {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"},
- {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"},
- {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"},
- {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
+ {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"},
+ {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"},
+ {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"},
+ {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"},
+ {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"},
+ {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"},
+ {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"},
+ {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"},
+ {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"},
+ {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"},
+ {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"},
+ {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"},
+ {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"},
+ {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"},
+ {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"},
+ {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"},
+ {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"},
+ {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"},
+ {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"},
+ {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"},
+ {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"},
+ {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"},
+ {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"},
+ {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"},
+ {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"},
+ {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"},
+ {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"},
+ {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"},
+ {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"},
+ {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"},
+ {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"},
+ {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"},
+ {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"},
+ {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"},
+ {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"},
+ {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"},
+ {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"},
+ {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"},
+ {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"},
+ {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"},
+ {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"},
]
ecdsa = [
- {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
- {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
+ {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
+ {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
]
embit = [
{file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"},
]
-enum34 = [
- {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"},
- {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"},
- {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"},
-]
environs = [
- {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
- {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
+ {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
+ {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
]
fastapi = [
- {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"},
- {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
-]
-grpcio = [
- {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
- {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"},
- {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"},
- {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"},
- {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"},
- {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"},
- {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"},
- {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"},
- {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"},
- {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"},
- {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"},
- {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"},
- {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"},
- {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"},
- {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"},
- {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"},
- {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"},
- {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"},
- {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"},
- {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"},
- {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"},
- {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"},
- {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"},
- {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"},
- {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"},
- {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"},
- {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"},
- {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"},
- {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"},
- {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"},
- {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"},
- {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"},
- {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"},
+ {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
+ {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -1503,12 +1131,12 @@ httpx = [
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
]
idna = [
- {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
- {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+ {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
+ {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
- {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
+ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
+ {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1518,63 +1146,92 @@ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
-Jinja2 = [
- {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
- {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
+jinja2 = [
+ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+ {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
]
lnurl = [
{file = "lnurl-0.3.6-py3-none-any.whl", hash = "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92"},
{file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"},
]
loguru = [
- {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
- {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
+ {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
+ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
]
-MarkupSafe = [
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
- {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
marshmallow = [
- {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"},
- {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"},
+ {file = "marshmallow-3.17.0-py3-none-any.whl", hash = "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb"},
+ {file = "marshmallow-3.17.0.tar.gz", hash = "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"},
]
mock = [
{file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"},
@@ -1610,20 +1267,16 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
outcome = [
- {file = "outcome-1.2.0-py2.py3-none-any.whl", hash = "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"},
- {file = "outcome-1.2.0.tar.gz", hash = "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672"},
+ {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
+ {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
-pathlib2 = [
- {file = "pathlib2-2.3.7.post1-py2.py3-none-any.whl", hash = "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b"},
- {file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"},
-]
pathspec = [
- {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
- {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
+ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
+ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
@@ -1633,79 +1286,43 @@ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
-protobuf = [
- {file = "protobuf-4.21.6-cp310-abi3-win32.whl", hash = "sha256:49f88d56a9180dbb7f6199c920f5bb5c1dd0172f672983bb281298d57c2ac8eb"},
- {file = "protobuf-4.21.6-cp310-abi3-win_amd64.whl", hash = "sha256:7a6cc8842257265bdfd6b74d088b829e44bcac3cca234c5fdd6052730017b9ea"},
- {file = "protobuf-4.21.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ba596b9ffb85c909fcfe1b1a23136224ed678af3faf9912d3fa483d5f9813c4e"},
- {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4143513c766db85b9d7c18dbf8339673c8a290131b2a0fe73855ab20770f72b0"},
- {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6cea204865595a92a7b240e4b65bcaaca3ad5d2ce25d9db3756eba06041138e"},
- {file = "protobuf-4.21.6-cp37-cp37m-win32.whl", hash = "sha256:9666da97129138585b26afcb63ad4887f602e169cafe754a8258541c553b8b5d"},
- {file = "protobuf-4.21.6-cp37-cp37m-win_amd64.whl", hash = "sha256:308173d3e5a3528787bb8c93abea81d5a950bdce62840d9760effc84127fb39c"},
- {file = "protobuf-4.21.6-cp38-cp38-win32.whl", hash = "sha256:aa29113ec901281f29d9d27b01193407a98aa9658b8a777b0325e6d97149f5ce"},
- {file = "protobuf-4.21.6-cp38-cp38-win_amd64.whl", hash = "sha256:8f9e60f7d44592c66e7b332b6a7b4b6e8d8b889393c79dbc3a91f815118f8eac"},
- {file = "protobuf-4.21.6-cp39-cp39-win32.whl", hash = "sha256:80e6540381080715fddac12690ee42d087d0d17395f8d0078dfd6f1181e7be4c"},
- {file = "protobuf-4.21.6-cp39-cp39-win_amd64.whl", hash = "sha256:77b355c8604fe285536155286b28b0c4cbc57cf81b08d8357bf34829ea982860"},
- {file = "protobuf-4.21.6-py2.py3-none-any.whl", hash = "sha256:07a0bb9cc6114f16a39c866dc28b6e3d96fa4ffb9cc1033057412547e6e75cb9"},
- {file = "protobuf-4.21.6-py3-none-any.whl", hash = "sha256:c7c864148a237f058c739ae7a05a2b403c0dfa4ce7d1f3e5213f352ad52d57c6"},
- {file = "protobuf-4.21.6.tar.gz", hash = "sha256:6b1040a5661cd5f6e610cbca9cfaa2a17d60e2bb545309bc1b278bb05be44bdd"},
-]
psycopg2-binary = [
- {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"},
+ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win32.whl", hash = "sha256:ebccf1123e7ef66efc615a68295bf6fdba875a75d5bba10a05073202598085fc"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:1f6ca4a9068f5c5c57e744b4baa79f40e83e3746875cac3c45467b16326bab45"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
@@ -1745,54 +1362,28 @@ pycryptodomex = [
{file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
]
pydantic = [
- {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
- {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
- {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
- {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
- {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
- {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
- {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
- {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
- {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
- {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
-]
-pyln-bolt7 = [
- {file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"},
- {file = "pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5"},
-]
-pyln-client = [
- {file = "pyln-client-0.12.1.tar.gz", hash = "sha256:f14fa7947b65ecde2753984452441cf41b7b25b1a0ba7beced48786fa54d2bfe"},
- {file = "pyln_client-0.12.1-py3-none-any.whl", hash = "sha256:6b500bcc49e4028d50692b962d9c9f7e9ede920d718f9b9412f04f7db0aa0e63"},
-]
-pyln-proto = [
- {file = "pyln-proto-0.12.0.tar.gz", hash = "sha256:3214d99d8385f2135a94937f0dc1da626a33b257e9ebc320841656edaefabbe5"},
- {file = "pyln_proto-0.12.0-py3-none-any.whl", hash = "sha256:dedef5d8e476a9ade5a0b2eb919ccc37e4a57f2a78fdc399f1c5e0de17e41604"},
+ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+ {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+ {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+ {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+ {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+ {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+ {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+ {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+ {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
@@ -1801,21 +1392,16 @@ pyparsing = [
pypng = [
{file = "pypng-0.0.21-py3-none-any.whl", hash = "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"},
]
-PyQRCode = [
+pyqrcode = [
{file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"},
{file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"},
]
-pyScss = [
- {file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"},
-]
-PySocks = [
- {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
- {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
- {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
+pyscss = [
+ {file = "pyScss-1.3.7.tar.gz", hash = "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"},
]
pytest = [
- {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
- {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
+ {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
+ {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
]
pytest-asyncio = [
{file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"},
@@ -1826,10 +1412,10 @@ pytest-cov = [
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
python-dotenv = [
- {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
- {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
+ {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
+ {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
]
-PyYAML = [
+pyyaml = [
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
{file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
{file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
@@ -1860,14 +1446,10 @@ PyYAML = [
{file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
-Represent = [
+represent = [
{file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"},
{file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"},
]
-requests = [
- {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
- {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
-]
rfc3986 = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
@@ -1897,10 +1479,6 @@ secp256k1 = [
{file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4"},
{file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
]
-setuptools = [
- {file = "setuptools-65.4.0-py3-none-any.whl", hash = "sha256:c2d2709550f15aab6c9110196ea312f468f41cd546bceb24127a1be6fdcaeeb1"},
- {file = "setuptools-65.4.0.tar.gz", hash = "sha256:a8f6e213b4b0661f590ccf40de95d28a177cd747d098624ad3f69c40287297e9"},
-]
shortuuid = [
{file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
{file = "shortuuid-1.0.1.tar.gz", hash = "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f"},
@@ -1910,44 +1488,48 @@ six = [
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sniffio = [
- {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
- {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
+ {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
]
-SQLAlchemy = [
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"},
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"},
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-win32.whl", hash = "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-win_amd64.whl", hash = "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-win32.whl", hash = "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-win_amd64.whl", hash = "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-win32.whl", hash = "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-win_amd64.whl", hash = "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-win32.whl", hash = "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-win_amd64.whl", hash = "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-win32.whl", hash = "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"},
- {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"},
+sqlalchemy = [
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"},
+ {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"},
]
sqlalchemy-aio = [
{file = "sqlalchemy_aio-0.17.0-py3-none-any.whl", hash = "sha256:3f4aa392c38f032d6734826a4138a0f02ed3122d442ed142be1e5964f2a33b60"},
@@ -1991,20 +1573,17 @@ typed-ast = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
]
types-protobuf = [
- {file = "types-protobuf-3.20.4.tar.gz", hash = "sha256:0dad3a5009895c985a56e2837f61902bad9594151265ac0ee907bb16d0b01eb7"},
- {file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"},
+ {file = "types-protobuf-3.19.22.tar.gz", hash = "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"},
+ {file = "types_protobuf-3.19.22-py3-none-any.whl", hash = "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab"},
]
typing-extensions = [
- {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
- {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
-]
-urllib3 = [
- {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
- {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
+ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+ {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
]
uvicorn = [
- {file = "uvicorn-0.18.3-py3-none-any.whl", hash = "sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af"},
- {file = "uvicorn-0.18.3.tar.gz", hash = "sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b"},
+ {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"},
+ {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
@@ -2064,6 +1643,6 @@ win32-setctime = [
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
zipp = [
- {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"},
- {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"},
+ {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
+ {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
]
diff --git a/pyproject.toml b/pyproject.toml
index f106350c..1ae8c1fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,59 +11,55 @@ script = "build.py"
[tool.poetry.dependencies]
python = "^3.9 | ^3.8 | ^3.7"
aiofiles = "0.8.0"
-asgiref = "^3.4.1"
-attrs = "22.1.0"
+asgiref = "3.4.1"
+attrs = "21.2.0"
bech32 = "1.2.0"
bitstring = "3.1.9"
cerberus = "1.3.4"
-certifi = "2022.9.24"
-charset-normalizer = "2.0.12"
-click = "8.0.4"
-ecdsa = "0.18.0"
+certifi = "2021.5.30"
+charset-normalizer = "2.0.6"
+click = "8.0.1"
+ecdsa = "0.17.0"
embit = "0.4.9"
-environs = "9.5.0"
-fastapi = "0.83.0"
+environs = "9.3.3"
+fastapi = "0.78.0"
h11 = "0.12.0"
+httpcore = "0.15.0"
httptools = "0.4.0"
httpx = "0.23.0"
-idna = "3.4"
-importlib-metadata = "^4.8.1"
-jinja2 = "3.0.3"
+idna = "3.2"
+importlib-metadata = "4.8.1"
+jinja2 = "3.0.1"
lnurl = "0.3.6"
-markupsafe = "2.1.1"
-marshmallow = "3.18.0"
-outcome = "1.2.0"
-psycopg2-binary = "2.9.3"
+markupsafe = "2.0.1"
+marshmallow = "3.17.0"
+outcome = "1.1.0"
+psycopg2-binary = "2.9.1"
pycryptodomex = "3.14.1"
-pydantic = "1.10.2"
+pydantic = "1.8.2"
pypng = "0.0.21"
pyqrcode = "1.2.1"
-pyScss = "1.4.0"
-python-dotenv = "0.21.0"
+pyscss = "1.3.7"
+python-dotenv = "0.19.0"
pyyaml = "5.4.1"
represent = "1.6.0.post0"
rfc3986 = "1.5.0"
secp256k1 = "0.14.0"
shortuuid = "1.0.1"
six = "1.16.0"
-sniffio = "1.3.0"
-sqlalchemy = "1.3.24"
+sniffio = "1.2.0"
+sqlalchemy = "1.3.23"
sqlalchemy-aio = "0.17.0"
sse-starlette = "0.6.2"
-typing-extensions = "4.3.0"
-uvicorn = "0.18.3"
+typing-extensions = "3.10.0.2"
+uvicorn = "0.18.1"
uvloop = "0.16.0"
watchgod = "0.7"
websockets = "10.0"
-zipp = "^3.5.0"
-loguru = "0.6.0"
-cffi = "1.15.1"
+zipp = "3.5.0"
+loguru = "0.5.3"
+cffi = "1.15.0"
websocket-client = "1.3.3"
-grpcio = "^1.49.1"
-protobuf = "^4.21.6"
-pyln-client = "^0.12.0"
-httpcore = "0.15.0"
-cashu = {path = "../cashu/dist/cashu-0.1.11-py3-none-any.whl"}
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From 45f2fffe90cc30b18e8ea7c510c1f3c36734d7cf Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 30 Sep 2022 12:25:48 +0100
Subject: [PATCH 0015/1058] Added the cashu router
---
lnbits/extensions/cashu/__init__.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index fa549ad2..ed67b134 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -6,20 +6,20 @@ from lnbits.db import Database
from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
+from cashu.mint.router import router as cashu_router
+
db = Database("ext_cashu")
-cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["TPoS"])
-
+cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
+cashu_ext.include_router(router=cashu_router)
def cashu_renderer():
return template_renderer(["lnbits/extensions/cashu/templates"])
-
from .tasks import wait_for_paid_invoices
from .views import * # noqa
from .views_api import * # noqa
-
def cashu_start():
loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
From a6ddc0b335a5909b81b8700654f9814e0dafcb8d Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 30 Sep 2022 12:26:50 +0100
Subject: [PATCH 0016/1058] Extension authors
---
lnbits/extensions/cashu/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index c688b22c..4f097e8b 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -2,5 +2,5 @@
"name": "Cashu Ecash",
"short_description": "Ecash mints with LN peg in/out",
"icon": "approval",
- "contributors": ["shinobi", "arcbtc", "calle"]
+ "contributors": ["arcbtc", "calle"]
}
From b09dbc4b85d5cd0cd69ea6ea00f100f7d334baf4 Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 30 Sep 2022 14:23:03 +0100
Subject: [PATCH 0017/1058] started adding mint stuff
---
lnbits/extensions/cashu/crud.py | 59 ++++++
lnbits/extensions/cashu/ledger.py | 282 +++++++++++++++++++++++++++
lnbits/extensions/cashu/models.py | 105 +++++++++-
lnbits/extensions/cashu/views_api.py | 67 ++++++-
4 files changed, 510 insertions(+), 3 deletions(-)
create mode 100644 lnbits/extensions/cashu/ledger.py
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index f50da111..e1cdaf4e 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -68,3 +68,62 @@ async def get_cashus(wallet_ids: Union[str, List[str]]) -> List[Cashu]:
async def delete_cashu(cashu_id: str) -> None:
await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,))
+
+
+
+###############MINT STUFF#################
+
+async def store_promise(
+ amount: int,
+ B_: str,
+ C_: str,
+ db: Database,
+ conn: Optional[Connection] = None,
+):
+
+ await (conn or db).execute(
+ """
+ INSERT INTO promises
+ (amount, B_b, C_b)
+ VALUES (?, ?, ?)
+ """,
+ (
+ amount,
+ str(B_),
+ str(C_),
+ ),
+ )
+
+
+async def get_proofs_used(
+ db: Database,
+ conn: Optional[Connection] = None,
+):
+
+ rows = await (conn or db).fetchall(
+ """
+ SELECT secret from proofs_used
+ """
+ )
+ return [row[0] for row in rows]
+
+
+async def invalidate_proof(
+ proof: Proof,
+ db: Database,
+ conn: Optional[Connection] = None,
+):
+
+ # we add the proof and secret to the used list
+ await (conn or db).execute(
+ """
+ INSERT INTO proofs_used
+ (amount, C, secret)
+ VALUES (?, ?, ?)
+ """,
+ (
+ proof.amount,
+ str(proof.C),
+ str(proof.secret),
+ ),
+ )
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/ledger.py b/lnbits/extensions/cashu/ledger.py
new file mode 100644
index 00000000..cc5ef924
--- /dev/null
+++ b/lnbits/extensions/cashu/ledger.py
@@ -0,0 +1,282 @@
+import hashlib
+from typing import List, Set
+
+from models import BlindedMessage, BlindedSignature, Invoice, Proof
+from secp256k1 import PublicKey, PrivateKey
+
+from lnbits.core.services import check_transaction_status, create_invoice
+
+class Ledger:
+ def __init__(self, secret_key: str, db: str, MAX_ORDER: int = Query(64)):
+ self.proofs_used: Set[str] = set()
+
+ self.master_key: str = secret_key
+ self.keys: List[PrivateKey] = self._derive_keys(self.master_key)
+ self.pub_keys: List[PublicKey] = self._derive_pubkeys(self.keys)
+ self.db: Database = Database("mint", db)
+
+ async def load_used_proofs(self):
+ self.proofs_used = set(await get_proofs_used(db=self.db))
+
+ @staticmethod
+ def _derive_keys(master_key: str):
+ """Deterministic derivation of keys for 2^n values."""
+ return {
+ 2
+ ** i: PrivateKey(
+ hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")[:32],
+ raw=True,
+ )
+ for i in range(MAX_ORDER)
+ }
+
+ @staticmethod
+ def _derive_pubkeys(keys: List[PrivateKey]):
+ return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
+
+ async def _generate_promises(self, amounts: List[int], B_s: List[str]):
+ """Generates promises that sum to the given amount."""
+ return [
+ await self._generate_promise(amount, PublicKey(bytes.fromhex(B_), raw=True))
+ for (amount, B_) in zip(amounts, B_s)
+ ]
+
+ async def _generate_promise(self, amount: int, B_: PublicKey):
+ """Generates a promise for given amount and returns a pair (amount, C')."""
+ secret_key = self.keys[amount] # Get the correct key
+ C_ = step2_bob(B_, secret_key)
+ await store_promise(
+ amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), db=self.db
+ )
+ return BlindedSignature(amount=amount, C_=C_.serialize().hex())
+
+ def _check_spendable(self, proof: Proof):
+ """Checks whether the proof was already spent."""
+ return not proof.secret in self.proofs_used
+
+ def _verify_proof(self, proof: Proof):
+ """Verifies that the proof of promise was issued by this ledger."""
+ if not self._check_spendable(proof):
+ raise Exception(f"tokens already spent. Secret: {proof.secret}")
+ secret_key = self.keys[proof.amount] # Get the correct key to check against
+ C = PublicKey(bytes.fromhex(proof.C), raw=True)
+ return verify(secret_key, C, proof.secret)
+
+ def _verify_outputs(
+ self, total: int, amount: int, output_data: List[BlindedMessage]
+ ):
+ """Verifies the expected split was correctly computed"""
+ fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
+ fst_outputs = amount_split(fst_amt)
+ snd_outputs = amount_split(snd_amt)
+ expected = fst_outputs + snd_outputs
+ given = [o.amount for o in output_data]
+ return given == expected
+
+ def _verify_no_duplicates(
+ self, proofs: List[Proof], output_data: List[BlindedMessage]
+ ):
+ secrets = [p.secret for p in proofs]
+ if len(secrets) != len(list(set(secrets))):
+ return False
+ B_s = [od.B_ for od in output_data]
+ if len(B_s) != len(list(set(B_s))):
+ return False
+ return True
+
+ def _verify_split_amount(self, amount: int):
+ """Split amount like output amount can't be negative or too big."""
+ try:
+ self._verify_amount(amount)
+ except:
+ # For better error message
+ raise Exception("invalid split amount: " + str(amount))
+
+ def _verify_amount(self, amount: int):
+ """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
+ valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
+ if not valid:
+ raise Exception("invalid amount: " + str(amount))
+ return amount
+
+ def _verify_equation_balanced(
+ self, proofs: List[Proof], outs: List[BlindedMessage]
+ ):
+ """Verify that Σoutputs - Σinputs = 0."""
+ sum_inputs = sum(self._verify_amount(p.amount) for p in proofs)
+ sum_outputs = sum(self._verify_amount(p.amount) for p in outs)
+ assert sum_outputs - sum_inputs == 0
+
+ def _get_output_split(self, amount: int):
+ """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
+ self._verify_amount(amount)
+ bits_amt = bin(amount)[::-1][:-2]
+ rv = []
+ for (pos, bit) in enumerate(bits_amt):
+ if bit == "1":
+ rv.append(2**pos)
+ return rv
+
+ async def _invalidate_proofs(self, proofs: List[Proof]):
+ """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
+ # Mark proofs as used and prepare new promises
+ proof_msgs = set([p.secret for p in proofs])
+ self.proofs_used |= proof_msgs
+ # store in db
+ for p in proofs:
+ await invalidate_proof(p, db=self.db)
+
+ # Public methods
+ def get_pubkeys(self):
+ """Returns public keys for possible amounts."""
+ return {a: p.serialize().hex() for a, p in self.pub_keys.items()}
+
+ async def request_mint(self, amount):
+ """Returns Lightning invoice and stores it in the db."""
+ payment_request, payment_hash = payment_hash, payment_request = await create_invoice(
+ wallet_id=link.wallet,
+ amount=amount,
+ memo=link.description,
+ unhashed_description=link.description.encode("utf-8"),
+ extra={
+ "tag": "Cashu"
+ },
+ )
+
+ invoice = Invoice(
+ amount=amount, pr=payment_request, hash=payment_hash, issued=False
+ )
+ if not payment_request or not payment_hash:
+ raise Exception(f"Could not create Lightning invoice.")
+ await store_lightning_invoice(invoice, db=self.db)
+ return payment_request, payment_hash
+
+ async def mint(self, B_s: List[PublicKey], amounts: List[int], payment_hash=None):
+ """Mints a promise for coins for B_."""
+ # check if lightning invoice was paid
+ if payment_hash and not await check_transaction_status(ayment_hash)
+ raise Exception("Lightning invoice not paid yet.")
+
+ for amount in amounts:
+ if amount not in [2**i for i in range(MAX_ORDER)]:
+ raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
+
+ promises = [
+ await self._generate_promise(amount, B_) for B_, amount in zip(B_s, amounts)
+ ]
+ return promises
+
+ async def melt(self, proofs: List[Proof], amount: int, invoice: str):
+ """Invalidates proofs and pays a Lightning invoice."""
+ # if not LIGHTNING:
+ total = sum([p["amount"] for p in proofs])
+ # check that lightning fees are included
+ assert total + fee_reserve(amount * 1000) >= amount, Exception(
+ "provided proofs not enough for Lightning payment."
+ )
+
+ status, payment_hash = await pay_invoice(
+ wallet_id=link.wallet,
+ payment_request=invoice,
+ max_sat=amount,
+ extra={"tag": "Ecash melt"},
+ )
+
+ if status == True:
+ await self._invalidate_proofs(proofs)
+ return status, payment_hash
+
+ async def check_spendable(self, proofs: List[Proof]):
+ """Checks if all provided proofs are valid and still spendable (i.e. have not been spent)."""
+ return {i: self._check_spendable(p) for i, p in enumerate(proofs)}
+
+ async def split(
+ self, proofs: List[Proof], amount: int, output_data: List[BlindedMessage]
+ ):
+ """Consumes proofs and prepares new promises based on the amount split."""
+ self._verify_split_amount(amount)
+ # Verify proofs are valid
+ if not all([self._verify_proof(p) for p in proofs]):
+ return False
+
+ total = sum([p.amount for p in proofs])
+
+ if not self._verify_no_duplicates(proofs, output_data):
+ raise Exception("duplicate proofs or promises")
+ if amount > total:
+ raise Exception("split amount is higher than the total sum")
+ if not self._verify_outputs(total, amount, output_data):
+ raise Exception("split of promises is not as expected")
+
+ # Mark proofs as used and prepare new promises
+ await self._invalidate_proofs(proofs)
+
+ outs_fst = amount_split(total - amount)
+ outs_snd = amount_split(amount)
+ B_fst = [od.B_ for od in output_data[: len(outs_fst)]]
+ B_snd = [od.B_ for od in output_data[len(outs_fst) :]]
+ prom_fst, prom_snd = await self._generate_promises(
+ outs_fst, B_fst
+ ), await self._generate_promises(outs_snd, B_snd)
+ self._verify_equation_balanced(proofs, prom_fst + prom_snd)
+ return prom_fst, prom_snd
+
+
+#######FUNCTIONS###############
+def fee_reserve(amount_msat: int) -> int:
+ """Function for calculating the Lightning fee reserve"""
+ return max(
+ int(LIGHTNING_RESERVE_FEE_MIN), int(amount_msat * LIGHTNING_FEE_PERCENT / 100.0)
+ )
+
+def amount_split(amount):
+ """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
+ bits_amt = bin(amount)[::-1][:-2]
+ rv = []
+ for (pos, bit) in enumerate(bits_amt):
+ if bit == "1":
+ rv.append(2**pos)
+ return rv
+
+def hash_to_point(secret_msg):
+ """Generates x coordinate from the message hash and checks if the point lies on the curve.
+ If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
+ point = None
+ msg = secret_msg
+ while point is None:
+ _hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
+ try:
+ # We construct compressed pub which has x coordinate encoded with even y
+ _hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
+ _hash[0] = 0x02 # set first byte to represent even y coord
+ _hash = bytes(_hash)
+ point = PublicKey(_hash, raw=True)
+ except:
+ msg = _hash
+
+ return point
+
+
+def step1_alice(secret_msg):
+ secret_msg = secret_msg.encode("utf-8")
+ Y = hash_to_point(secret_msg)
+ r = PrivateKey()
+ B_ = Y + r.pubkey
+ return B_, r
+
+
+def step2_bob(B_, a):
+ C_ = B_.mult(a)
+ return C_
+
+
+def step3_alice(C_, r, A):
+ C = C_ - A.mult(r)
+ return C
+
+
+def verify(a, C, secret_msg):
+ Y = hash_to_point(secret_msg.encode("utf-8"))
+ return C == Y.mult(a)
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 892abdf1..ba9303c5 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -31,5 +31,106 @@ class Pegs(BaseModel):
def from_row(cls, row: Row) -> "TPoS":
return cls(**dict(row))
-class PayLnurlWData(BaseModel):
- lnurl: str
\ No newline at end of file
+
+class Proof(BaseModel):
+ amount: int
+ secret: str
+ C: str
+ reserved: bool = False # whether this proof is reserved for sending
+ send_id: str = "" # unique ID of send attempt
+ time_created: str = ""
+ time_reserved: str = ""
+
+ @classmethod
+ def from_row(cls, row: Row):
+ return cls(
+ amount=row[0],
+ C=row[1],
+ secret=row[2],
+ reserved=row[3] or False,
+ send_id=row[4] or "",
+ time_created=row[5] or "",
+ time_reserved=row[6] or "",
+ )
+
+ @classmethod
+ def from_dict(cls, d: dict):
+ assert "secret" in d, "no secret in proof"
+ assert "amount" in d, "no amount in proof"
+ return cls(
+ amount=d.get("amount"),
+ C=d.get("C"),
+ secret=d.get("secret"),
+ reserved=d.get("reserved") or False,
+ send_id=d.get("send_id") or "",
+ time_created=d.get("time_created") or "",
+ time_reserved=d.get("time_reserved") or "",
+ )
+
+ def to_dict(self):
+ return dict(amount=self.amount, secret=self.secret, C=self.C)
+
+ def __getitem__(self, key):
+ return self.__getattribute__(key)
+
+ def __setitem__(self, key, val):
+ self.__setattr__(key, val)
+
+
+class Proofs(BaseModel):
+ """TODO: Use this model"""
+
+ proofs: List[Proof]
+
+
+class Invoice(BaseModel):
+ amount: int
+ pr: str
+ hash: str
+ issued: bool = False
+
+ @classmethod
+ def from_row(cls, row: Row):
+ return cls(
+ amount=int(row[0]),
+ pr=str(row[1]),
+ hash=str(row[2]),
+ issued=bool(row[3]),
+ )
+
+
+class BlindedMessage(BaseModel):
+ amount: int
+ B_: str
+
+
+class BlindedSignature(BaseModel):
+ amount: int
+ C_: str
+
+ @classmethod
+ def from_dict(cls, d: dict):
+ return cls(
+ amount=d["amount"],
+ C_=d["C_"],
+ )
+
+
+class MintPayloads(BaseModel):
+ blinded_messages: List[BlindedMessage] = []
+
+
+class SplitPayload(BaseModel):
+ proofs: List[Proof]
+ amount: int
+ output_data: MintPayloads
+
+
+class CheckPayload(BaseModel):
+ proofs: List[Proof]
+
+
+class MeltPayload(BaseModel):
+ proofs: List[Proof]
+ amount: int
+ invoice: str
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 49383945..5a1c500e 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -1,4 +1,6 @@
from http import HTTPStatus
+from secp256k1 import PublicKey
+from typing import Union
import httpx
from fastapi import Query
@@ -14,8 +16,9 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import cashu_ext
from .crud import create_cashu, delete_cashu, get_cashu, get_cashus, update_cashu_keys
-from .models import Cashu, Pegs, PayLnurlWData
+from .models import Cashu, Pegs, CheckPayload, MeltPayload, MintPayloads, SplitPayload
+import .ledger
@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
async def api_cashus(
@@ -168,3 +171,65 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
logger.error(exc)
return {"paid": False}
return status
+
+
+#################CASHU STUFF###################
+
+@cashu_ext.get("/keys")
+def keys():
+ """Get the public keys of the mint"""
+ return ledger.get_pubkeys()
+
+
+@cashu_ext.get("/mint")
+async def request_mint(amount: int = 0):
+ """Request minting of tokens. Server responds with a Lightning invoice."""
+ payment_request, payment_hash = await ledger.request_mint(amount)
+ print(f"Lightning invoice: {payment_request}")
+ return {"pr": payment_request, "hash": payment_hash}
+
+
+@cashu_ext.post("/mint")
+async def mint(payloads: MintPayloads, payment_hash: Union[str, None] = None):
+ 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 {"error": str(exc)}
+
+
+@cashu_ext.post("/melt")
+async def melt(payload: MeltPayload):
+
+ ok, preimage = await ledger.melt(payload.proofs, payload.amount, payload.invoice)
+ return {"paid": ok, "preimage": preimage}
+
+
+@cashu_ext.post("/check")
+async def check_spendable(payload: CheckPayload):
+ return await ledger.check_spendable(payload.proofs)
+
+
+@cashu_ext.post("/split")
+async def split(payload: SplitPayload):
+ """
+ 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
+ output_data = payload.output_data.blinded_messages
+ try:
+ split_return = await ledger.split(proofs, amount, output_data)
+ except Exception as exc:
+ return {"error": str(exc)}
+ if not split_return:
+ """There was a problem with the split"""
+ raise Exception("could not split tokens.")
+ fst_promises, snd_promises = split_return
+ return {"fst": fst_promises, "snd": snd_promises}
\ No newline at end of file
From 77269ad0a7b05127a917738e52d8119ab8f1d57c Mon Sep 17 00:00:00 2001
From: ben
Date: Mon, 3 Oct 2022 10:45:13 +0100
Subject: [PATCH 0018/1058] Booting
---
lnbits/extensions/cashu/__init__.py | 3 ---
lnbits/extensions/cashu/crud.py | 15 ++++-----------
lnbits/extensions/cashu/ledger.py | 19 ++++++++++---------
lnbits/extensions/cashu/models.py | 4 +++-
lnbits/extensions/cashu/views_api.py | 4 ++--
5 files changed, 19 insertions(+), 26 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index ed67b134..cf277664 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -6,12 +6,9 @@ from lnbits.db import Database
from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
-from cashu.mint.router import router as cashu_router
-
db = Database("ext_cashu")
cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
-cashu_ext.include_router(router=cashu_router)
def cashu_renderer():
return template_renderer(["lnbits/extensions/cashu/templates"])
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index e1cdaf4e..2892e6a4 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -3,7 +3,7 @@ from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
-from .models import Cashu, Pegs
+from .models import Cashu, Pegs, Proof
from embit import script
from embit import ec
@@ -76,9 +76,7 @@ async def delete_cashu(cashu_id: str) -> None:
async def store_promise(
amount: int,
B_: str,
- C_: str,
- db: Database,
- conn: Optional[Connection] = None,
+ C_: str
):
await (conn or db).execute(
@@ -95,10 +93,7 @@ async def store_promise(
)
-async def get_proofs_used(
- db: Database,
- conn: Optional[Connection] = None,
-):
+async def get_proofs_used():
rows = await (conn or db).fetchall(
"""
@@ -109,9 +104,7 @@ async def get_proofs_used(
async def invalidate_proof(
- proof: Proof,
- db: Database,
- conn: Optional[Connection] = None,
+ proof: Proof
):
# we add the proof and secret to the used list
diff --git a/lnbits/extensions/cashu/ledger.py b/lnbits/extensions/cashu/ledger.py
index cc5ef924..59cc3b92 100644
--- a/lnbits/extensions/cashu/ledger.py
+++ b/lnbits/extensions/cashu/ledger.py
@@ -1,22 +1,23 @@
import hashlib
from typing import List, Set
-from models import BlindedMessage, BlindedSignature, Invoice, Proof
+from .models import BlindedMessage, BlindedSignature, Invoice, Proof
from secp256k1 import PublicKey, PrivateKey
+from fastapi import Query
+
from lnbits.core.services import check_transaction_status, create_invoice
class Ledger:
- def __init__(self, secret_key: str, db: str, MAX_ORDER: int = Query(64)):
+ def __init__(self, secret_key: str, MAX_ORDER: int = Query(64)):
self.proofs_used: Set[str] = set()
self.master_key: str = secret_key
self.keys: List[PrivateKey] = self._derive_keys(self.master_key)
self.pub_keys: List[PublicKey] = self._derive_pubkeys(self.keys)
- self.db: Database = Database("mint", db)
async def load_used_proofs(self):
- self.proofs_used = set(await get_proofs_used(db=self.db))
+ self.proofs_used = set(await get_proofs_used)
@staticmethod
def _derive_keys(master_key: str):
@@ -48,7 +49,7 @@ class Ledger:
secret_key = self.keys[amount] # Get the correct key
C_ = step2_bob(B_, secret_key)
await store_promise(
- amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), db=self.db
+ amount, B_=B_.serialize().hex(), C_=C_.serialize().hex()
)
return BlindedSignature(amount=amount, C_=C_.serialize().hex())
@@ -126,7 +127,7 @@ class Ledger:
self.proofs_used |= proof_msgs
# store in db
for p in proofs:
- await invalidate_proof(p, db=self.db)
+ await invalidate_proof(p)
# Public methods
def get_pubkeys(self):
@@ -150,13 +151,13 @@ class Ledger:
)
if not payment_request or not payment_hash:
raise Exception(f"Could not create Lightning invoice.")
- await store_lightning_invoice(invoice, db=self.db)
+ await store_lightning_invoice(invoice)
return payment_request, payment_hash
async def mint(self, B_s: List[PublicKey], amounts: List[int], payment_hash=None):
"""Mints a promise for coins for B_."""
# check if lightning invoice was paid
- if payment_hash and not await check_transaction_status(ayment_hash)
+ if payment_hash and not await check_transaction_status(payment_hash):
raise Exception("Lightning invoice not paid yet.")
for amount in amounts:
@@ -224,7 +225,7 @@ class Ledger:
return prom_fst, prom_snd
-#######FUNCTIONS###############
+##############FUNCTIONS###############
def fee_reserve(amount_msat: int) -> int:
"""Function for calculating the Lightning fee reserve"""
return max(
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index ba9303c5..a673dfe7 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -1,5 +1,5 @@
from sqlite3 import Row
-from typing import Optional
+from typing import Optional, List
from fastapi import Query
from pydantic import BaseModel
@@ -31,6 +31,8 @@ class Pegs(BaseModel):
def from_row(cls, row: Row) -> "TPoS":
return cls(**dict(row))
+class PayLnurlWData(BaseModel):
+ lnurl: str
class Proof(BaseModel):
amount: int
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 5a1c500e..5cc6e271 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -16,9 +16,9 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import cashu_ext
from .crud import create_cashu, delete_cashu, get_cashu, get_cashus, update_cashu_keys
-from .models import Cashu, Pegs, CheckPayload, MeltPayload, MintPayloads, SplitPayload
+from .models import Cashu, Pegs, CheckPayload, MeltPayload, MintPayloads, SplitPayload, PayLnurlWData
-import .ledger
+from .ledger import Ledger, fee_reserve, amount_split, hash_to_point, step1_alice, step2_bob, step3_alice, verify
@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
async def api_cashus(
From d51103e7e8e4052081fa5a944a75828bb6674d40 Mon Sep 17 00:00:00 2001
From: ben
Date: Mon, 3 Oct 2022 14:43:02 +0100
Subject: [PATCH 0019/1058] Working through, getting functions working
---
lnbits/extensions/cashu/crud.py | 85 ++--
lnbits/extensions/cashu/ledger.py | 426 +++++++++---------
lnbits/extensions/cashu/migrations.py | 31 +-
lnbits/extensions/cashu/models.py | 9 +-
lnbits/extensions/cashu/tasks.py | 1 -
.../cashu/templates/cashu/index.html | 7 +-
lnbits/extensions/cashu/views.py | 1 -
lnbits/extensions/cashu/views_api.py | 51 ++-
8 files changed, 354 insertions(+), 257 deletions(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 2892e6a4..7a9c25c3 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -1,24 +1,37 @@
+import os
+
from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
-from .models import Cashu, Pegs, Proof
+from .models import Cashu, Pegs, Proof, Promises
from embit import script
from embit import ec
from embit.networks import NETWORKS
+from embit import bip32
+from embit import bip39
from binascii import unhexlify, hexlify
+import random
+
+from loguru import logger
async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
cashu_id = urlsafe_short_hash()
- prv = ec.PrivateKey.from_wif(urlsafe_short_hash())
- pub = prv.get_public_key()
+
+ entropy = bytes([random.getrandbits(8) for i in range(16)])
+ mnemonic = bip39.mnemonic_from_bytes(entropy)
+ seed = bip39.mnemonic_to_seed(mnemonic)
+ root = bip32.HDKey.from_seed(seed, version=NETWORKS["main"]["xprv"])
+
+ bip44_xprv = root.derive("m/44h/1h/0h")
+ bip44_xpub = bip44_xprv.to_public()
await db.execute(
"""
INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, prvkey, pubkey)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
cashu_id,
@@ -28,8 +41,8 @@ async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
data.fraction,
data.maxsats,
data.coins,
- prv,
- pub
+ bip44_xprv.to_base58(),
+ bip44_xpub.to_base58()
),
)
@@ -39,17 +52,20 @@ async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
async def update_cashu_keys(cashu_id, wif: str = None) -> Optional[Cashu]:
- if not wif:
- prv = ec.PrivateKey.from_wif(urlsafe_short_hash())
- else:
- prv = ec.PrivateKey.from_wif(wif)
- pub = prv.get_public_key()
- await db.execute("UPDATE cashu.cashu SET prv = ?, pub = ? WHERE id = ?", (hexlify(prv.serialize()), hexlify(pub.serialize()), cashu_id))
+ entropy = bytes([random.getrandbits(8) for i in range(16)])
+ mnemonic = bip39.mnemonic_from_bytes(entropy)
+ seed = bip39.mnemonic_to_seed(mnemonic)
+ root = bip32.HDKey.from_seed(seed, version=NETWORKS["main"]["xprv"])
+
+ bip44_xprv = root.derive("m/44h/1h/0h")
+ bip44_xpub = bip44_xprv.to_public()
+
+ await db.execute("UPDATE cashu.cashu SET prv = ?, pub = ? WHERE id = ?", bip44_xprv.to_base58(), bip44_xpub.to_base58(), cashu_id)
row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
return Cashu(**row) if row else None
-async def get_cashu(cashu_id: str) -> Optional[Cashu]:
+async def get_cashu(cashu_id) -> Optional[Cashu]:
row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
return Cashu(**row) if row else None
@@ -66,57 +82,62 @@ async def get_cashus(wallet_ids: Union[str, List[str]]) -> List[Cashu]:
return [Cashu(**row) for row in rows]
-async def delete_cashu(cashu_id: str) -> None:
+async def delete_cashu(cashu_id) -> None:
await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,))
-
+##########################################
###############MINT STUFF#################
+##########################################
async def store_promise(
amount: int,
B_: str,
- C_: str
+ C_: str,
+ cashu_id
):
+ promise_id = urlsafe_short_hash()
await (conn or db).execute(
"""
- INSERT INTO promises
- (amount, B_b, C_b)
- VALUES (?, ?, ?)
+ INSERT INTO cashu.promises
+ (id, amount, B_b, C_b, cashu_id)
+ VALUES (?, ?, ?, ?, ?)
""",
(
+ promise_id,
amount,
str(B_),
str(C_),
+ cashu_id
),
)
+async def get_promises(cashu_id) -> Optional[Cashu]:
+ row = await db.fetchall("SELECT * FROM cashu.promises WHERE cashu_id = ?", (promises_id,))
+ return Promises(**row) if row else None
-async def get_proofs_used():
-
- rows = await (conn or db).fetchall(
- """
- SELECT secret from proofs_used
- """
- )
+async def get_proofs_used(cashu_id):
+ rows = await db.fetchall("SELECT secret from cashu.proofs_used WHERE id = ?", (cashu_id,))
return [row[0] for row in rows]
async def invalidate_proof(
- proof: Proof
+ proof: Proof,
+ cashu_id
):
-
- # we add the proof and secret to the used list
+ invalidate_proof_id = urlsafe_short_hash()
await (conn or db).execute(
"""
- INSERT INTO proofs_used
- (amount, C, secret)
- VALUES (?, ?, ?)
+ INSERT INTO cashu.proofs_used
+ (id, amount, C, secret, cashu_id)
+ VALUES (?, ?, ?, ?, ?)
""",
(
+ invalidate_proof_id,
proof.amount,
str(proof.C),
str(proof.secret),
+ cashu_id
),
)
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/ledger.py b/lnbits/extensions/cashu/ledger.py
index 59cc3b92..404f7ee8 100644
--- a/lnbits/extensions/cashu/ledger.py
+++ b/lnbits/extensions/cashu/ledger.py
@@ -5,234 +5,239 @@ from .models import BlindedMessage, BlindedSignature, Invoice, Proof
from secp256k1 import PublicKey, PrivateKey
from fastapi import Query
-
+from .crud import get_cashu
from lnbits.core.services import check_transaction_status, create_invoice
-class Ledger:
- def __init__(self, secret_key: str, MAX_ORDER: int = Query(64)):
- self.proofs_used: Set[str] = set()
-
- self.master_key: str = secret_key
- self.keys: List[PrivateKey] = self._derive_keys(self.master_key)
- self.pub_keys: List[PublicKey] = self._derive_pubkeys(self.keys)
-
- async def load_used_proofs(self):
- self.proofs_used = set(await get_proofs_used)
-
- @staticmethod
- def _derive_keys(master_key: str):
- """Deterministic derivation of keys for 2^n values."""
- return {
- 2
- ** i: PrivateKey(
- hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
- .hexdigest()
- .encode("utf-8")[:32],
- raw=True,
- )
- for i in range(MAX_ORDER)
- }
-
- @staticmethod
- def _derive_pubkeys(keys: List[PrivateKey]):
- return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
-
- async def _generate_promises(self, amounts: List[int], B_s: List[str]):
- """Generates promises that sum to the given amount."""
- return [
- await self._generate_promise(amount, PublicKey(bytes.fromhex(B_), raw=True))
- for (amount, B_) in zip(amounts, B_s)
- ]
-
- async def _generate_promise(self, amount: int, B_: PublicKey):
- """Generates a promise for given amount and returns a pair (amount, C')."""
- secret_key = self.keys[amount] # Get the correct key
- C_ = step2_bob(B_, secret_key)
- await store_promise(
- amount, B_=B_.serialize().hex(), C_=C_.serialize().hex()
+def _derive_keys(master_key: str, cashu_id: str = Query(None)):
+ """Deterministic derivation of keys for 2^n values."""
+ return {
+ 2
+ ** i: PrivateKey(
+ hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")[:32],
+ raw=True,
)
- return BlindedSignature(amount=amount, C_=C_.serialize().hex())
+ for i in range(MAX_ORDER)
+ }
- def _check_spendable(self, proof: Proof):
- """Checks whether the proof was already spent."""
- return not proof.secret in self.proofs_used
+def _derive_pubkeys(keys: List[PrivateKey], cashu_id: str = Query(None)):
+ return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
- def _verify_proof(self, proof: Proof):
- """Verifies that the proof of promise was issued by this ledger."""
- if not self._check_spendable(proof):
- raise Exception(f"tokens already spent. Secret: {proof.secret}")
- secret_key = self.keys[proof.amount] # Get the correct key to check against
- C = PublicKey(bytes.fromhex(proof.C), raw=True)
- return verify(secret_key, C, proof.secret)
+async def _generate_promises(amounts: List[int], B_s: List[str], cashu_id: str = Query(None)):
+ """Generates promises that sum to the given amount."""
+ return [
+ await self._generate_promise(amount, PublicKey(bytes.fromhex(B_), raw=True))
+ for (amount, B_) in zip(amounts, B_s)
+ ]
- def _verify_outputs(
- self, total: int, amount: int, output_data: List[BlindedMessage]
- ):
- """Verifies the expected split was correctly computed"""
- fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
- fst_outputs = amount_split(fst_amt)
- snd_outputs = amount_split(snd_amt)
- expected = fst_outputs + snd_outputs
- given = [o.amount for o in output_data]
- return given == expected
+async def _generate_promise(amount: int, B_: PublicKey, cashu_id: str = Query(None)):
+ """Generates a promise for given amount and returns a pair (amount, C')."""
+ secret_key = self.keys[amount] # Get the correct key
+ C_ = step2_bob(B_, secret_key)
+ await store_promise(
+ amount, B_=B_.serialize().hex(), C_=C_.serialize().hex()
+ )
+ return BlindedSignature(amount=amount, C_=C_.serialize().hex())
- def _verify_no_duplicates(
- self, proofs: List[Proof], output_data: List[BlindedMessage]
- ):
- secrets = [p.secret for p in proofs]
- if len(secrets) != len(list(set(secrets))):
- return False
- B_s = [od.B_ for od in output_data]
- if len(B_s) != len(list(set(B_s))):
- return False
- return True
+def _check_spendable(proof: Proof, cashu_id: str = Query(None)):
+ """Checks whether the proof was already spent."""
+ return not proof.secret in self.proofs_used
- def _verify_split_amount(self, amount: int):
- """Split amount like output amount can't be negative or too big."""
- try:
- self._verify_amount(amount)
- except:
- # For better error message
- raise Exception("invalid split amount: " + str(amount))
+def _verify_proof(proof: Proof, cashu_id: str = Query(None)):
+ """Verifies that the proof of promise was issued by this ledger."""
+ if not self._check_spendable(proof):
+ raise Exception(f"tokens already spent. Secret: {proof.secret}")
+ secret_key = self.keys[proof.amount] # Get the correct key to check against
+ C = PublicKey(bytes.fromhex(proof.C), raw=True)
+ return verify(secret_key, C, proof.secret)
- def _verify_amount(self, amount: int):
- """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
- valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
- if not valid:
- raise Exception("invalid amount: " + str(amount))
- return amount
+def _verify_outputs(total: int, amount: int, output_data: List[BlindedMessage], cashu_id: str = Query(None)):
+ """Verifies the expected split was correctly computed"""
+ fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
+ fst_outputs = amount_split(fst_amt)
+ snd_outputs = amount_split(snd_amt)
+ expected = fst_outputs + snd_outputs
+ given = [o.amount for o in output_data]
+ return given == expected
- def _verify_equation_balanced(
- self, proofs: List[Proof], outs: List[BlindedMessage]
- ):
- """Verify that Σoutputs - Σinputs = 0."""
- sum_inputs = sum(self._verify_amount(p.amount) for p in proofs)
- sum_outputs = sum(self._verify_amount(p.amount) for p in outs)
- assert sum_outputs - sum_inputs == 0
+def _verify_no_duplicates(proofs: List[Proof], output_data: List[BlindedMessage], cashu_id: str = Query(None)):
+ secrets = [p.secret for p in proofs]
+ if len(secrets) != len(list(set(secrets))):
+ return False
+ B_s = [od.B_ for od in output_data]
+ if len(B_s) != len(list(set(B_s))):
+ return False
+ return True
- def _get_output_split(self, amount: int):
- """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
+def _verify_split_amount(amount: int, cashu_id: str = Query(None)):
+ """Split amount like output amount can't be negative or too big."""
+ try:
self._verify_amount(amount)
- bits_amt = bin(amount)[::-1][:-2]
- rv = []
- for (pos, bit) in enumerate(bits_amt):
- if bit == "1":
- rv.append(2**pos)
- return rv
+ except:
+ # For better error message
+ raise Exception("invalid split amount: " + str(amount))
- async def _invalidate_proofs(self, proofs: List[Proof]):
- """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
- # Mark proofs as used and prepare new promises
- proof_msgs = set([p.secret for p in proofs])
- self.proofs_used |= proof_msgs
- # store in db
- for p in proofs:
- await invalidate_proof(p)
+def _verify_amount(amount: int, cashu_id: str = Query(None)):
+ """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
+ valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
+ if not valid:
+ raise Exception("invalid amount: " + str(amount))
+ return amount
- # Public methods
- def get_pubkeys(self):
- """Returns public keys for possible amounts."""
- return {a: p.serialize().hex() for a, p in self.pub_keys.items()}
+def _verify_equation_balanced(proofs: List[Proof], outs: List[BlindedMessage], cashu_id: str = Query(None)):
+ """Verify that Σoutputs - Σinputs = 0."""
+ sum_inputs = sum(self._verify_amount(p.amount) for p in proofs)
+ sum_outputs = sum(self._verify_amount(p.amount) for p in outs)
+ assert sum_outputs - sum_inputs == 0
- async def request_mint(self, amount):
- """Returns Lightning invoice and stores it in the db."""
- payment_request, payment_hash = payment_hash, payment_request = await create_invoice(
- wallet_id=link.wallet,
- amount=amount,
- memo=link.description,
- unhashed_description=link.description.encode("utf-8"),
- extra={
- "tag": "Cashu"
- },
- )
+def _get_output_split(amount: int, cashu_id: str):
+ """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
+ self._verify_amount(amount)
+ bits_amt = bin(amount)[::-1][:-2]
+ rv = []
+ for (pos, bit) in enumerate(bits_amt):
+ if bit == "1":
+ rv.append(2**pos)
+ return rv
- invoice = Invoice(
- amount=amount, pr=payment_request, hash=payment_hash, issued=False
- )
- if not payment_request or not payment_hash:
- raise Exception(f"Could not create Lightning invoice.")
- await store_lightning_invoice(invoice)
- return payment_request, payment_hash
+async def _invalidate_proofs(proofs: List[Proof], cashu_id: str = Query(None)):
+ """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
+ # Mark proofs as used and prepare new promises
+ proof_msgs = set([p.secret for p in proofs])
+ self.proofs_used |= proof_msgs
+ # store in db
+ for p in proofs:
+ await invalidate_proof(p)
- async def mint(self, B_s: List[PublicKey], amounts: List[int], payment_hash=None):
- """Mints a promise for coins for B_."""
- # check if lightning invoice was paid
- if payment_hash and not await check_transaction_status(payment_hash):
+def get_pubkeys(cashu_id: str = Query(None)):
+ """Returns public keys for possible amounts."""
+ return {a: p.serialize().hex() for a, p in self.pub_keys.items()}
+
+async def request_mint(amount, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
+ """Returns Lightning invoice and stores it in the db."""
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=cashu.wallet,
+ amount=amount,
+ memo=cashu.name,
+ unhashed_description=cashu.name.encode("utf-8"),
+ extra={
+ "tag": "Cashu"
+ },
+ )
+
+ invoice = Invoice(
+ amount=amount, pr=payment_request, hash=payment_hash, issued=False
+ )
+ if not payment_request or not payment_hash:
+ raise Exception(f"Could not create Lightning invoice.")
+ return payment_request, payment_hash
+
+async def mint(B_s: List[PublicKey], amounts: List[int], payment_hash: str = Query(None), cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
+ """Mints a promise for coins for B_."""
+ # check if lightning invoice was paid
+ if payment_hash:
+ if not await check_transaction_status(wallet_id=cashu.wallet, payment_hash=payment_hash):
raise Exception("Lightning invoice not paid yet.")
- for amount in amounts:
- if amount not in [2**i for i in range(MAX_ORDER)]:
- raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
+ for amount in amounts:
+ if amount not in [2**i for i in range(MAX_ORDER)]:
+ raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
- promises = [
- await self._generate_promise(amount, B_) for B_, amount in zip(B_s, amounts)
- ]
- return promises
+ promises = [
+ await self._generate_promise(amount, B_) for B_, amount in zip(B_s, amounts)
+ ]
+ return promises
- async def melt(self, proofs: List[Proof], amount: int, invoice: str):
- """Invalidates proofs and pays a Lightning invoice."""
- # if not LIGHTNING:
- total = sum([p["amount"] for p in proofs])
- # check that lightning fees are included
- assert total + fee_reserve(amount * 1000) >= amount, Exception(
- "provided proofs not enough for Lightning payment."
- )
+async def melt(proofs: List[Proof], amount: int, invoice: str, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
+ """Invalidates proofs and pays a Lightning invoice."""
+ # if not LIGHTNING:
+ total = sum([p["amount"] for p in proofs])
+ # check that lightning fees are included
+ assert total + fee_reserve(amount * 1000) >= amount, Exception(
+ "provided proofs not enough for Lightning payment."
+ )
- status, payment_hash = await pay_invoice(
- wallet_id=link.wallet,
- payment_request=invoice,
- max_sat=amount,
- extra={"tag": "Ecash melt"},
- )
+ status, payment_hash = await pay_invoice(
+ wallet_id=link.wallet,
+ payment_request=invoice,
+ max_sat=amount,
+ extra={"tag": "Ecash melt"},
+ )
- if status == True:
- await self._invalidate_proofs(proofs)
- return status, payment_hash
-
- async def check_spendable(self, proofs: List[Proof]):
- """Checks if all provided proofs are valid and still spendable (i.e. have not been spent)."""
- return {i: self._check_spendable(p) for i, p in enumerate(proofs)}
-
- async def split(
- self, proofs: List[Proof], amount: int, output_data: List[BlindedMessage]
- ):
- """Consumes proofs and prepares new promises based on the amount split."""
- self._verify_split_amount(amount)
- # Verify proofs are valid
- if not all([self._verify_proof(p) for p in proofs]):
- return False
-
- total = sum([p.amount for p in proofs])
-
- if not self._verify_no_duplicates(proofs, output_data):
- raise Exception("duplicate proofs or promises")
- if amount > total:
- raise Exception("split amount is higher than the total sum")
- if not self._verify_outputs(total, amount, output_data):
- raise Exception("split of promises is not as expected")
-
- # Mark proofs as used and prepare new promises
+ if status == True:
await self._invalidate_proofs(proofs)
+ return status, payment_hash
- outs_fst = amount_split(total - amount)
- outs_snd = amount_split(amount)
- B_fst = [od.B_ for od in output_data[: len(outs_fst)]]
- B_snd = [od.B_ for od in output_data[len(outs_fst) :]]
- prom_fst, prom_snd = await self._generate_promises(
- outs_fst, B_fst
- ), await self._generate_promises(outs_snd, B_snd)
- self._verify_equation_balanced(proofs, prom_fst + prom_snd)
- return prom_fst, prom_snd
+async def check_spendable(proofs: List[Proof], cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
+ """Checks if all provided proofs are valid and still spendable (i.e. have not been spent)."""
+ return {i: self._check_spendable(p) for i, p in enumerate(proofs)}
+
+async def split(proofs: List[Proof], amount: int, output_data: List[BlindedMessage], cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
+ """Consumes proofs and prepares new promises based on the amount split."""
+ self._verify_split_amount(amount)
+ # Verify proofs are valid
+ if not all([self._verify_proof(p) for p in proofs]):
+ return False
+
+ total = sum([p.amount for p in proofs])
+
+ if not self._verify_no_duplicates(proofs, output_data):
+ raise Exception("duplicate proofs or promises")
+ if amount > total:
+ raise Exception("split amount is higher than the total sum")
+ if not self._verify_outputs(total, amount, output_data):
+ raise Exception("split of promises is not as expected")
+
+ # Mark proofs as used and prepare new promises
+ await self._invalidate_proofs(proofs)
+
+ outs_fst = amount_split(total - amount)
+ outs_snd = amount_split(amount)
+ B_fst = [od.B_ for od in output_data[: len(outs_fst)]]
+ B_snd = [od.B_ for od in output_data[len(outs_fst) :]]
+ prom_fst, prom_snd = await self._generate_promises(
+ outs_fst, B_fst
+ ), await self._generate_promises(outs_snd, B_snd)
+ self._verify_equation_balanced(proofs, prom_fst + prom_snd)
+ return prom_fst, prom_snd
-##############FUNCTIONS###############
-def fee_reserve(amount_msat: int) -> int:
+async def fee_reserve(amount_msat: int, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
"""Function for calculating the Lightning fee reserve"""
return max(
int(LIGHTNING_RESERVE_FEE_MIN), int(amount_msat * LIGHTNING_FEE_PERCENT / 100.0)
)
-def amount_split(amount):
+async def amount_split(amount, cashu_id: str):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
"""Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
bits_amt = bin(amount)[::-1][:-2]
rv = []
@@ -241,7 +246,11 @@ def amount_split(amount):
rv.append(2**pos)
return rv
-def hash_to_point(secret_msg):
+async def hash_to_point(secret_msg, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
"""Generates x coordinate from the message hash and checks if the point lies on the curve.
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
point = None
@@ -260,24 +269,39 @@ def hash_to_point(secret_msg):
return point
-def step1_alice(secret_msg):
+async def step1_alice(secret_msg, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
secret_msg = secret_msg.encode("utf-8")
Y = hash_to_point(secret_msg)
r = PrivateKey()
B_ = Y + r.pubkey
return B_, r
+async def step2_bob(B_, a, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
-def step2_bob(B_, a):
C_ = B_.mult(a)
return C_
-def step3_alice(C_, r, A):
+async def step3_alice(C_, r, A, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
C = C_ - A.mult(r)
return C
-def verify(a, C, secret_msg):
+async def verify(a, C, secret_msg, cashu_id: str = Query(None)):
+ cashu = await get_cashu(cashu_id)
+ if not cashu:
+ raise Exception(f"Could not find Cashu")
+
Y = hash_to_point(secret_msg.encode("utf-8"))
return C == Y.mult(a)
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index 53420062..f7d8f4f0 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -8,7 +8,7 @@ async def m001_initial(db):
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
- tickershort TEXT NOT NULL,
+ tickershort TEXT DEFAULT 'sats',
fraction BOOL,
maxsats INT,
coins INT,
@@ -32,3 +32,32 @@ async def m001_initial(db):
"""
)
+ """
+ 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
+ );
+ """
+ )
+
+ """
+ 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
+ );
+ """
+ )
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index a673dfe7..094966ff 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -9,7 +9,7 @@ class Cashu(BaseModel):
id: str = Query(None)
name: str = Query(None)
wallet: str = Query(None)
- tickershort: str
+ tickershort: str = Query(None)
fraction: bool = Query(None)
maxsats: int = Query(0)
coins: int = Query(0)
@@ -34,6 +34,13 @@ class Pegs(BaseModel):
class PayLnurlWData(BaseModel):
lnurl: str
+class Promises(BaseModel):
+ id: str
+ amount: int
+ B_b: str
+ C_b: str
+ cashu_id: str
+
class Proof(BaseModel):
amount: int
secret: str
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
index fe00a591..5fbdde8e 100644
--- a/lnbits/extensions/cashu/tasks.py
+++ b/lnbits/extensions/cashu/tasks.py
@@ -9,7 +9,6 @@ from lnbits.tasks import internal_invoice_queue, register_invoice_listener
from .crud import get_cashu
-
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
register_invoice_listener(invoice_queue)
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html
index 17b2a919..3cd57d45 100644
--- a/lnbits/extensions/cashu/templates/cashu/index.html
+++ b/lnbits/extensions/cashu/templates/cashu/index.html
@@ -80,13 +80,10 @@
-
-
Create Mint
+ :disable="formDialog.data.wallet == null || formDialog.data.name == null" type="submit">Create Mint
Cancel
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 4ac1f1ce..655ed028 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -22,7 +22,6 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
"cashu/index.html", {"request": request, "user": user.dict()}
)
-
@cashu_ext.get("/wallet")
async def cashu(request: Request):
return cashu_renderer().TemplateResponse("cashu/wallet.html",{"request": request})
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 5cc6e271..391aeda1 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -15,10 +15,25 @@ from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import cashu_ext
-from .crud import create_cashu, delete_cashu, get_cashu, get_cashus, update_cashu_keys
-from .models import Cashu, Pegs, CheckPayload, MeltPayload, MintPayloads, SplitPayload, PayLnurlWData
+from .ledger import get_pubkeys, request_mint, mint
-from .ledger import Ledger, fee_reserve, amount_split, hash_to_point, step1_alice, step2_bob, step3_alice, verify
+from .crud import (
+ create_cashu,
+ delete_cashu,
+ get_cashu,
+ get_cashus,
+ update_cashu_keys
+)
+
+from .models import (
+ Cashu,
+ Pegs,
+ CheckPayload,
+ MeltPayload,
+ MintPayloads,
+ SplitPayload,
+ PayLnurlWData
+)
@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
async def api_cashus(
@@ -173,50 +188,54 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
return status
-#################CASHU STUFF###################
+########################################
+#################MINT###################
+########################################
@cashu_ext.get("/keys")
-def keys():
+def keys(cashu_id: str):
"""Get the public keys of the mint"""
- return ledger.get_pubkeys()
+ return get_pubkeys(cashu_id)
@cashu_ext.get("/mint")
-async def request_mint(amount: int = 0):
+async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
"""Request minting of tokens. Server responds with a Lightning invoice."""
- payment_request, payment_hash = await ledger.request_mint(amount)
+ payment_request, payment_hash = await request_mint(amount, cashu_id)
print(f"Lightning invoice: {payment_request}")
return {"pr": payment_request, "hash": payment_hash}
@cashu_ext.post("/mint")
-async def mint(payloads: MintPayloads, payment_hash: Union[str, None] = None):
+async def mint_coins(payloads: MintPayloads, payment_hash: Union[str, None] = None, cashu_id: str = Query(None)):
amounts = []
B_s = []
for payload in payloads.blinded_messages:
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
+ promises = await mint(B_s, amounts, payment_hash, cashu_id)
+ logger.debug(promises)
try:
- promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
+ promises = await mint(B_s, amounts, payment_hash, cashu_id)
return promises
except Exception as exc:
return {"error": str(exc)}
@cashu_ext.post("/melt")
-async def melt(payload: MeltPayload):
+async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
- ok, preimage = await ledger.melt(payload.proofs, payload.amount, payload.invoice)
+ ok, preimage = await melt(payload.proofs, payload.amount, payload.invoice, cashu_id)
return {"paid": ok, "preimage": preimage}
@cashu_ext.post("/check")
-async def check_spendable(payload: CheckPayload):
- return await ledger.check_spendable(payload.proofs)
+async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(None)):
+ return await check_spendable(payload.proofs, cashu_id)
@cashu_ext.post("/split")
-async def split(payload: SplitPayload):
+async def spli_coinst(payload: SplitPayload, cashu_id: str = Query(None)):
"""
Requetst a set of tokens with amount "total" to be split into two
newly minted sets with amount "split" and "total-split".
@@ -225,7 +244,7 @@ async def split(payload: SplitPayload):
amount = payload.amount
output_data = payload.output_data.blinded_messages
try:
- split_return = await ledger.split(proofs, amount, output_data)
+ split_return = await split(proofs, amount, output_data)
except Exception as exc:
return {"error": str(exc)}
if not split_return:
From 3b70d45091e6d69aac04a207c59e1115eba8c76f Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Tue, 4 Oct 2022 09:56:15 +0200
Subject: [PATCH 0020/1058] .env variable for STARTUP_INVOICE_EXPIRY_CHECK
---
.env.example | 2 ++
lnbits/settings.py | 1 +
lnbits/tasks.py | 4 ++--
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/.env.example b/.env.example
index 93b82325..1c7d1529 100644
--- a/.env.example
+++ b/.env.example
@@ -32,6 +32,8 @@ LNBITS_SERVICE_FEE="0.0"
LNBITS_RESERVE_FEE_MIN=2000
# value in percent
LNBITS_RESERVE_FEE_PERCENT=1.0
+# check invoice expiry on every startup (can be slow on large instances)
+STARTUP_INVOICE_EXPIRY_CHECK=True
# Change theme
LNBITS_SITE_TITLE="LNbits"
diff --git a/lnbits/settings.py b/lnbits/settings.py
index 3f4e31cc..9fc3c197 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -64,6 +64,7 @@ PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True)
RESERVE_FEE_MIN = env.int("LNBITS_RESERVE_FEE_MIN", default=2000)
RESERVE_FEE_PERCENT = env.float("LNBITS_RESERVE_FEE_PERCENT", default=1.0)
SERVICE_FEE = env.float("LNBITS_SERVICE_FEE", default=0.0)
+STARTUP_INVOICE_EXPIRY_CHECK = env.bool("STARTUP_INVOICE_EXPIRY_CHECK", default=True)
try:
LNBITS_COMMIT = (
diff --git a/lnbits/tasks.py b/lnbits/tasks.py
index 94e43dcf..26758303 100644
--- a/lnbits/tasks.py
+++ b/lnbits/tasks.py
@@ -15,7 +15,7 @@ from lnbits.core.crud import (
get_standalone_payment,
)
from lnbits.core.services import redeem_lnurl_withdraw
-from lnbits.settings import WALLET
+from lnbits.settings import WALLET, STARTUP_INVOICE_EXPIRY_CHECK
from .core import db
@@ -144,7 +144,7 @@ async def check_pending_payments():
f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)"
)
# we delete expired invoices once upon the first pending check
- if incoming:
+ if incoming and STARTUP_INVOICE_EXPIRY_CHECK:
logger.debug("Task: deleting all expired invoices")
start_time: float = time.time()
await delete_expired_invoices(conn=conn)
From 166530eb0c985575a140124fb4c9e5a23ee9e5a7 Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Mar 2022 05:03:32 +0000
Subject: [PATCH 0021/1058] Added old admin extension
---
.env.example | 12 +-
lnbits/extensions/admin/README.md | 11 +
lnbits/extensions/admin/__init__.py | 10 +
lnbits/extensions/admin/config.json | 6 +
lnbits/extensions/admin/crud.py | 59 ++
lnbits/extensions/admin/migrations.py | 256 ++++++++
lnbits/extensions/admin/models.py | 38 ++
.../admin/templates/admin/index.html | 565 ++++++++++++++++++
lnbits/extensions/admin/views.py | 20 +
lnbits/extensions/admin/views_api.py | 41 ++
10 files changed, 1013 insertions(+), 5 deletions(-)
create mode 100644 lnbits/extensions/admin/README.md
create mode 100644 lnbits/extensions/admin/__init__.py
create mode 100644 lnbits/extensions/admin/config.json
create mode 100644 lnbits/extensions/admin/crud.py
create mode 100644 lnbits/extensions/admin/migrations.py
create mode 100644 lnbits/extensions/admin/models.py
create mode 100644 lnbits/extensions/admin/templates/admin/index.html
create mode 100644 lnbits/extensions/admin/views.py
create mode 100644 lnbits/extensions/admin/views_api.py
diff --git a/.env.example b/.env.example
index 93b82325..68e25ad1 100644
--- a/.env.example
+++ b/.env.example
@@ -3,10 +3,12 @@ PORT=5000
DEBUG=false
-LNBITS_ALLOWED_USERS=""
-LNBITS_ADMIN_USERS=""
-# Extensions only admin can access
-LNBITS_ADMIN_EXTENSIONS="ngrok"
+LNBITS_ADMIN_USERS="" # User IDs seperated by comma
+LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Extensions only admin can access
+
+LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
+
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
# csv ad image filepaths or urls, extensions can choose to honor
@@ -91,4 +93,4 @@ LNBITS_DENOMINATION=sats
# EclairWallet
ECLAIR_URL=http://127.0.0.1:8283
-ECLAIR_PASS=eclairpw
\ No newline at end of file
+ECLAIR_PASS=eclairpw
diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md
new file mode 100644
index 00000000..27729459
--- /dev/null
+++ b/lnbits/extensions/admin/README.md
@@ -0,0 +1,11 @@
+Example Extension
+*tagline*
+This is an example extension to help you organise and build you own.
+
+Try to include an image
+
+
+
+If your extension has API endpoints, include useful ones here
+
+curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
new file mode 100644
index 00000000..d5f26c90
--- /dev/null
+++ b/lnbits/extensions/admin/__init__.py
@@ -0,0 +1,10 @@
+from quart import Blueprint
+from lnbits.db import Database
+
+db = Database("ext_admin")
+
+admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates")
+
+
+from .views_api import * # noqa
+from .views import * # noqa
diff --git a/lnbits/extensions/admin/config.json b/lnbits/extensions/admin/config.json
new file mode 100644
index 00000000..69661733
--- /dev/null
+++ b/lnbits/extensions/admin/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Admin",
+ "short_description": "Manage your LNbits install",
+ "icon": "build",
+ "contributors": ["benarc"]
+}
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
new file mode 100644
index 00000000..cb8f9b5b
--- /dev/null
+++ b/lnbits/extensions/admin/crud.py
@@ -0,0 +1,59 @@
+from typing import List, Optional
+
+from . import db
+from .models import Admin, Funding
+from lnbits.settings import *
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.core.crud import create_payment
+from lnbits.db import Connection
+
+
+def update_wallet_balance(wallet_id: str, amount: int) -> str:
+ temp_id = f"temp_{urlsafe_short_hash()}"
+ internal_id = f"internal_{urlsafe_short_hash()}"
+ create_payment(
+ wallet_id=wallet_id,
+ checking_id=internal_id,
+ payment_request="admin_internal",
+ payment_hash="admin_internal",
+ amount=amount * 1000,
+ memo="Admin top up",
+ pending=False,
+ )
+ return "success"
+
+
+async def update_admin(
+) -> Optional[Admin]:
+ if not CLightningWallet:
+ print("poo")
+ await db.execute(
+ """
+ UPDATE admin
+ SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ?
+ WHERE 1
+ """,
+ (
+
+ ),
+ )
+ row = await db.fetchone("SELECT * FROM admin WHERE 1")
+ return Admin.from_row(row) if row else None
+
+async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
+ )
+ row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
+ return Jukebox(**row) if row else None
+
+async def get_admin() -> List[Admin]:
+ row = await db.fetchone("SELECT * FROM admin WHERE 1")
+ return Admin.from_row(row) if row else None
+
+
+async def get_funding() -> List[Funding]:
+ rows = await db.fetchall("SELECT * FROM funding")
+
+ return [Funding.from_row(row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
new file mode 100644
index 00000000..82d934cb
--- /dev/null
+++ b/lnbits/extensions/admin/migrations.py
@@ -0,0 +1,256 @@
+from sqlalchemy.exc import OperationalError # type: ignore
+from os import getenv
+from lnbits.helpers import urlsafe_short_hash
+
+
+async def m001_create_admin_table(db):
+ user = None
+ site_title = None
+ site_tagline = None
+ site_description = None
+ allowed_users = None
+ admin_user = None
+ default_wallet_name = None
+ data_folder = None
+ disabled_ext = None
+ force_https = True
+ service_fee = 0
+ funding_source = ""
+
+ if getenv("LNBITS_SITE_TITLE"):
+ site_title = getenv("LNBITS_SITE_TITLE")
+
+ if getenv("LNBITS_SITE_TAGLINE"):
+ site_tagline = getenv("LNBITS_SITE_TAGLINE")
+
+ if getenv("LNBITS_SITE_DESCRIPTION"):
+ site_description = getenv("LNBITS_SITE_DESCRIPTION")
+
+ if getenv("LNBITS_ALLOWED_USERS"):
+ allowed_users = getenv("LNBITS_ALLOWED_USERS")
+
+ if getenv("LNBITS_ADMIN_USER"):
+ admin_user = getenv("LNBITS_ADMIN_USER")
+
+ if getenv("LNBITS_DEFAULT_WALLET_NAME"):
+ default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
+
+ if getenv("LNBITS_DATA_FOLDER"):
+ data_folder = getenv("LNBITS_DATA_FOLDER")
+
+ if getenv("LNBITS_DISABLED_EXTENSIONS"):
+ disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+
+ if getenv("LNBITS_FORCE_HTTPS"):
+ force_https = getenv("LNBITS_FORCE_HTTPS")
+
+ if getenv("LNBITS_SERVICE_FEE"):
+ service_fee = getenv("LNBITS_SERVICE_FEE")
+
+ if getenv("LNBITS_BACKEND_WALLET_CLASS"):
+ funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
+
+ await db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS admin (
+ user TEXT,
+ site_title TEXT,
+ site_tagline TEXT,
+ site_description TEXT,
+ admin_user TEXT,
+ allowed_users TEXT,
+ default_wallet_name TEXT,
+ data_folder TEXT,
+ disabled_ext TEXT,
+ force_https BOOLEAN,
+ service_fee INT,
+ funding_source TEXT
+ );
+ """
+ )
+ await db.execute(
+ """
+ INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ user,
+ site_title,
+ site_tagline,
+ site_description,
+ admin_user,
+ allowed_users,
+ default_wallet_name,
+ data_folder,
+ disabled_ext,
+ force_https,
+ service_fee,
+ funding_source,
+ ),
+ )
+
+
+async def m001_create_funding_table(db):
+
+ funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
+
+ # Make the funding table, if it does not already exist
+ await db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS funding (
+ id TEXT PRIMARY KEY,
+ backend_wallet TEXT,
+ endpoint TEXT,
+ port INT,
+ read_key TEXT,
+ invoice_key TEXT,
+ admin_key TEXT,
+ cert TEXT,
+ balance INT,
+ selected INT
+ );
+ """
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, selected)
+ VALUES (?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "CLightningWallet",
+ getenv("CLIGHTNING_RPC"),
+ 1 if funding_wallet == "CLightningWallet" else 0,
+ ),
+ )
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "SparkWallet",
+ getenv("SPARK_URL"),
+ getenv("SPARK_TOKEN"),
+ 1 if funding_wallet == "SparkWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LnbitsWallet",
+ getenv("LNBITS_ENDPOINT"),
+ getenv("LNBITS_KEY"),
+ 1 if funding_wallet == "LnbitsWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LndWallet",
+ getenv("LND_GRPC_ENDPOINT"),
+ getenv("LND_GRPC_PORT"),
+ getenv("LND_GRPC_MACAROON"),
+ getenv("LND_GRPC_CERT"),
+ 1 if funding_wallet == "LndWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LndRestWallet",
+ getenv("LND_REST_ENDPOINT"),
+ getenv("LND_REST_MACAROON"),
+ getenv("LND_REST_CERT"),
+ 1 if funding_wallet == "LndWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LNPayWallet",
+ getenv("LNPAY_API_ENDPOINT"),
+ getenv("LNPAY_WALLET_KEY"),
+ getenv("LNPAY_API_KEY"), # this is going in as the cert
+ 1 if funding_wallet == "LNPayWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LntxbotWallet",
+ getenv("LNTXBOT_API_ENDPOINT"),
+ getenv("LNTXBOT_KEY"),
+ 1 if funding_wallet == "LntxbotWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "OpenNodeWallet",
+ getenv("OPENNODE_API_ENDPOINT"),
+ getenv("OPENNODE_KEY"),
+ 1 if funding_wallet == "OpenNodeWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "SparkWallet",
+ getenv("SPARK_URL"),
+ getenv("SPARK_TOKEN"),
+ 1 if funding_wallet == "SparkWallet" else 0,
+ ),
+ )
+
+ ## PLACEHOLDER FOR ECLAIR WALLET
+ # await db.execute(
+ # """
+ # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ # VALUES (?, ?, ?, ?, ?)
+ # """,
+ # (
+ # urlsafe_short_hash(),
+ # "EclairWallet",
+ # getenv("ECLAIR_URL"),
+ # getenv("ECLAIR_PASS"),
+ # 1 if funding_wallet == "EclairWallet" else 0,
+ # ),
+ # )
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
new file mode 100644
index 00000000..c38f17f4
--- /dev/null
+++ b/lnbits/extensions/admin/models.py
@@ -0,0 +1,38 @@
+from typing import NamedTuple
+from sqlite3 import Row
+
+class Admin(NamedTuple):
+ user: str
+ site_title: str
+ site_tagline: str
+ site_description:str
+ allowed_users: str
+ admin_user: str
+ default_wallet_name: str
+ data_folder: str
+ disabled_ext: str
+ force_https: str
+ service_fee: str
+ funding_source: str
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Admin":
+ data = dict(row)
+ return cls(**data)
+
+class Funding(NamedTuple):
+ id: str
+ backend_wallet: str
+ endpoint: str
+ port: str
+ read_key: str
+ invoice_key: str
+ admin_key: str
+ cert: str
+ balance: int
+ selected: int
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Funding":
+ data = dict(row)
+ return cls(**data)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
new file mode 100644
index 00000000..87cf09ef
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -0,0 +1,565 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+Admin
+
+
+
+
+
+
+ Settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Wallet topup
+
+
+
+
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+{% endblock %}
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
new file mode 100644
index 00000000..5e17919c
--- /dev/null
+++ b/lnbits/extensions/admin/views.py
@@ -0,0 +1,20 @@
+from quart import g, render_template, request, jsonify
+import json
+
+from lnbits.decorators import check_user_exists, validate_uuids
+from lnbits.extensions.admin import admin_ext
+from lnbits.core.crud import get_user, create_account
+from .crud import get_admin, get_funding
+from lnbits.settings import WALLET
+
+
+@admin_ext.route("/")
+@validate_uuids(["usr"], required=True)
+@check_user_exists()
+async def index():
+ user_id = g.user
+ admin = await get_admin()
+
+ funding = [{**funding._asdict()} for funding in await get_funding()]
+
+ return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
new file mode 100644
index 00000000..2a61b6f5
--- /dev/null
+++ b/lnbits/extensions/admin/views_api.py
@@ -0,0 +1,41 @@
+from quart import jsonify, g, request
+from http import HTTPStatus
+from .crud import update_wallet_balance
+from lnbits.extensions.admin import admin_ext
+from lnbits.decorators import api_check_wallet_key, api_validate_post_request
+from lnbits.core.crud import get_wallet
+from .crud import get_admin,update_admin
+import json
+
+@admin_ext.route("/api/v1/admin//", methods=["GET"])
+@api_check_wallet_key("admin")
+async def api_update_balance(wallet_id, topup_amount):
+ print(g.data.wallet)
+ try:
+ wallet = await get_wallet(wallet_id)
+ except:
+ return (
+ jsonify({"error": "Not allowed: not an admin"}),
+ HTTPStatus.FORBIDDEN,
+ )
+ print(wallet)
+ print(topup_amount)
+ return jsonify({"status": "Success"}), HTTPStatus.OK
+
+
+@admin_ext.route("/api/v1/admin/", methods=["POST"])
+@api_check_wallet_key("admin")
+@api_validate_post_request(schema={})
+async def api_update_admin():
+ body = await request.get_json()
+ admin = await get_admin()
+ print(g.wallet[2])
+ print(body["admin_user"])
+ if not admin.admin_user == g.wallet[2] and admin.admin_user != None:
+ return (
+ jsonify({"error": "Not allowed: not an admin"}),
+ HTTPStatus.FORBIDDEN,
+ )
+ updated = await update_admin(body)
+ print(updated)
+ return jsonify({"status": "Success"}), HTTPStatus.OK
\ No newline at end of file
From 68eee00b45170db4e35fa6fdafba85373958e9fa Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Mar 2022 05:11:55 +0000
Subject: [PATCH 0022/1058] old admin setup UI
---
lnbits/core/templates/core/admin.html | 717 ++++++++++++++++++++++++++
1 file changed, 717 insertions(+)
create mode 100644 lnbits/core/templates/core/admin.html
diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html
new file mode 100644
index 00000000..e8176555
--- /dev/null
+++ b/lnbits/core/templates/core/admin.html
@@ -0,0 +1,717 @@
+{% extends "public.html" %} {% from "macros.jinja" import window_vars with
+context %} {% block page %}
+
+
+
+
+
+ Welcome to LNbits
+
+
+ Fill in the information below to setup your LNbits instance. Details
+ can be changed later.
+
+
+
+
+
+
+ Branding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Service settings
+
+
+
+ Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
+ should be filled in for you
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View project in GitHub
+ Donate
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }}
+
+{% endblock %}
From 65e1f19ed1124340d2a9ba97b4420c099896488c Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:18:09 +0000
Subject: [PATCH 0023/1058] convert to FastAPI
---
lnbits/extensions/admin/__init__.py | 11 +++-
lnbits/extensions/admin/crud.py | 50 +++++++--------
lnbits/extensions/admin/migrations.py | 23 ++++---
lnbits/extensions/admin/models.py | 51 ++++++++++------
.../admin/templates/admin/index.html | 23 +++++--
lnbits/extensions/admin/views.py | 39 ++++++++----
lnbits/extensions/admin/views_api.py | 61 ++++++++++---------
7 files changed, 151 insertions(+), 107 deletions(-)
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
index d5f26c90..6a56b2bb 100644
--- a/lnbits/extensions/admin/__init__.py
+++ b/lnbits/extensions/admin/__init__.py
@@ -1,10 +1,15 @@
-from quart import Blueprint
+from fastapi import APIRouter
+
from lnbits.db import Database
+from lnbits.helpers import template_renderer
db = Database("ext_admin")
-admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates")
+admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"])
+
+def admin_renderer():
+ return template_renderer(["lnbits/extensions/admin/templates"])
-from .views_api import * # noqa
from .views import * # noqa
+from .views_api import * # noqa
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index cb8f9b5b..872d6c97 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,11 +1,11 @@
from typing import List, Optional
+from lnbits.core.crud import create_payment
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.settings import *
+
from . import db
from .models import Admin, Funding
-from lnbits.settings import *
-from lnbits.helpers import urlsafe_short_hash
-from lnbits.core.crud import create_payment
-from lnbits.db import Connection
def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -22,38 +22,30 @@ def update_wallet_balance(wallet_id: str, amount: int) -> str:
)
return "success"
-
-async def update_admin(
-) -> Optional[Admin]:
- if not CLightningWallet:
- print("poo")
- await db.execute(
- """
- UPDATE admin
- SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ?
- WHERE 1
- """,
- (
-
- ),
- )
- row = await db.fetchone("SELECT * FROM admin WHERE 1")
- return Admin.from_row(row) if row else None
-
-async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]:
+async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ print("UPDATE", q)
await db.execute(
- f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
+ f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
- return Jukebox(**row) if row else None
+ row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,))
+ assert row, "Newly updated settings couldn't be retrieved"
+ return Admin(**row) if row else None
+
+# async def update_admin(user: str, **kwargs) -> Optional[Admin]:
+# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+# await db.execute(
+# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user)
+# )
+# new_settings = await get_admin()
+# return new_settings
async def get_admin() -> List[Admin]:
- row = await db.fetchone("SELECT * FROM admin WHERE 1")
- return Admin.from_row(row) if row else None
+ row = await db.fetchone("SELECT * FROM admin")
+ return Admin(**row) if row else None
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM funding")
- return [Funding.from_row(row) for row in rows]
+ return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 82d934cb..13b76923 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -1,5 +1,7 @@
-from sqlalchemy.exc import OperationalError # type: ignore
from os import getenv
+
+from sqlalchemy.exc import OperationalError # type: ignore
+
from lnbits.helpers import urlsafe_short_hash
@@ -9,7 +11,7 @@ async def m001_create_admin_table(db):
site_tagline = None
site_description = None
allowed_users = None
- admin_user = None
+ admin_users = None
default_wallet_name = None
data_folder = None
disabled_ext = None
@@ -29,8 +31,9 @@ async def m001_create_admin_table(db):
if getenv("LNBITS_ALLOWED_USERS"):
allowed_users = getenv("LNBITS_ALLOWED_USERS")
- if getenv("LNBITS_ADMIN_USER"):
- admin_user = getenv("LNBITS_ADMIN_USER")
+ if getenv("LNBITS_ADMIN_USERS"):
+ admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
+ user = admin_users.split(',')[0]
if getenv("LNBITS_DEFAULT_WALLET_NAME"):
default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
@@ -53,32 +56,32 @@ async def m001_create_admin_table(db):
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
- user TEXT,
+ "user" TEXT,
site_title TEXT,
site_tagline TEXT,
site_description TEXT,
- admin_user TEXT,
+ admin_users TEXT,
allowed_users TEXT,
default_wallet_name TEXT,
data_folder TEXT,
disabled_ext TEXT,
force_https BOOLEAN,
- service_fee INT,
+ service_fee REAL,
funding_source TEXT
);
"""
)
await db.execute(
"""
- INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
+ INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
- user,
+ user.strip(),
site_title,
site_tagline,
site_description,
- admin_user,
+ admin_users[1:],
allowed_users,
default_wallet_name,
data_folder,
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index c38f17f4..4080ff01 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,18 +1,35 @@
-from typing import NamedTuple
from sqlite3 import Row
+from typing import List, Optional
-class Admin(NamedTuple):
+from fastapi import Query
+from pydantic import BaseModel
+
+
+class UpdateAdminSettings(BaseModel):
+ site_title: Optional[str]
+ site_tagline: Optional[str]
+ site_description: Optional[str]
+ allowed_users: Optional[str]
+ admin_users: Optional[str]
+ default_wallet_name: Optional[str]
+ data_folder: Optional[str]
+ disabled_ext: Optional[str]
+ force_https: Optional[bool]
+ service_fee: Optional[float]
+ funding_source: Optional[str]
+
+class Admin(BaseModel):
user: str
- site_title: str
- site_tagline: str
- site_description:str
- allowed_users: str
- admin_user: str
+ site_title: Optional[str]
+ site_tagline: Optional[str]
+ site_description: Optional[str]
+ allowed_users: Optional[str]
+ admin_users: str
default_wallet_name: str
data_folder: str
disabled_ext: str
- force_https: str
- service_fee: str
+ force_https: Optional[bool] = Query(True)
+ service_fee: float
funding_source: str
@classmethod
@@ -20,16 +37,16 @@ class Admin(NamedTuple):
data = dict(row)
return cls(**data)
-class Funding(NamedTuple):
+class Funding(BaseModel):
id: str
backend_wallet: str
- endpoint: str
- port: str
- read_key: str
- invoice_key: str
- admin_key: str
- cert: str
- balance: int
+ endpoint: str = Query(None)
+ port: str = Query(None)
+ read_key: str = Query(None)
+ invoice_key: str = Query(None)
+ admin_key: str = Query(None)
+ cert: str = Query(None)
+ balance: int = Query(None)
selected: int
@classmethod
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 87cf09ef..a6b45625 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -87,7 +87,7 @@
@@ -442,13 +442,14 @@
site_title: '{{admin.site_title}}',
tagline: '{{admin.site_tagline}}',
description: '{{admin.site_description}}',
- admin_user: '{{admin.admin_user}}',
- service_fee: parseInt('{{admin.service_fee}}'),
+ admin_users: '{{admin.admin_users}}',
+ service_fee: parseFloat('{{admin.service_fee}}'),
default_wallet_name: '{{admin.default_wallet_name}}',
data_folder: '{{admin.data_folder}}',
funding_source_primary: '{{admin.funding_source}}',
disabled_ext: '{{admin.disabled_ext}}'.split(','),
edited: [],
+ funding: {},
senddata: {}
}
},
@@ -528,15 +529,27 @@
},
UpdateLNbits: function () {
var self = this
- console.log(self.data.admin)
+ let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin
+ let data = {
+ site_title,
+ site_tagline: this.data.admin.tagline,
+ site_description: this.data.admin.description,
+ admin_users: admin_users.toString(),
+ default_wallet_name,
+ data_folder,
+ disabled_ext: disabled_ext.toString(),
+ service_fee,
+ funding_source: funding_source_primary}
+ console.log(data)
LNbits.api
.request(
'POST',
'/admin/api/v1/admin/',
self.g.user.wallets[0].adminkey,
- self.data.admin
+ data
)
.then(function (response) {
+ console.log(response.data)
self.$q.notify({
type: 'positive',
message:
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 5e17919c..00a0c99f 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -1,20 +1,33 @@
-from quart import g, render_template, request, jsonify
-import json
+from email.policy import default
+from os import getenv
-from lnbits.decorators import check_user_exists, validate_uuids
+from fastapi import Request
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.responses import HTMLResponse
+
+from lnbits.core.models import User
+from lnbits.decorators import check_user_exists
from lnbits.extensions.admin import admin_ext
-from lnbits.core.crud import get_user, create_account
+from lnbits.requestvars import g
+
+from . import admin_ext, admin_renderer
from .crud import get_admin, get_funding
-from lnbits.settings import WALLET
+templates = Jinja2Templates(directory="templates")
-@admin_ext.route("/")
-@validate_uuids(["usr"], required=True)
-@check_user_exists()
-async def index():
- user_id = g.user
+@admin_ext.get("/", response_class=HTMLResponse)
+async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
+ print(g())
+ funding = [f.dict() for f in await get_funding()]
- funding = [{**funding._asdict()} for funding in await get_funding()]
-
- return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding)
+ print("ADMIN", admin.dict())
+ return admin_renderer().TemplateResponse(
+ "admin/index.html", {
+ "request": request,
+ "user": user.dict(),
+ "admin": admin.dict(),
+ "funding": funding
+ }
+ )
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 2a61b6f5..b2c65be2 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,41 +1,42 @@
-from quart import jsonify, g, request
from http import HTTPStatus
-from .crud import update_wallet_balance
-from lnbits.extensions.admin import admin_ext
-from lnbits.decorators import api_check_wallet_key, api_validate_post_request
-from lnbits.core.crud import get_wallet
-from .crud import get_admin,update_admin
-import json
-@admin_ext.route("/api/v1/admin//", methods=["GET"])
-@api_check_wallet_key("admin")
-async def api_update_balance(wallet_id, topup_amount):
- print(g.data.wallet)
+from fastapi import Body, Depends, Request
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_wallet
+from lnbits.decorators import WalletTypeInfo, require_admin_key
+from lnbits.extensions.admin import admin_ext
+from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+
+from .crud import get_admin, update_admin, update_wallet_balance
+
+
+@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
+async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)):
+ print(g.wallet)
try:
wallet = await get_wallet(wallet_id)
except:
- return (
- jsonify({"error": "Not allowed: not an admin"}),
- HTTPStatus.FORBIDDEN,
- )
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
print(wallet)
print(topup_amount)
- return jsonify({"status": "Success"}), HTTPStatus.OK
+ return {"status": "Success"}
-@admin_ext.route("/api/v1/admin/", methods=["POST"])
-@api_check_wallet_key("admin")
-@api_validate_post_request(schema={})
-async def api_update_admin():
- body = await request.get_json()
+@admin_ext.post("/api/v1/admin/", status_code=HTTPStatus.OK)
+async def api_update_admin(
+ request: Request,
+ data: UpdateAdminSettings = Body(...),
+ g: WalletTypeInfo = Depends(require_admin_key)
+ ):
admin = await get_admin()
- print(g.wallet[2])
- print(body["admin_user"])
- if not admin.admin_user == g.wallet[2] and admin.admin_user != None:
- return (
- jsonify({"error": "Not allowed: not an admin"}),
- HTTPStatus.FORBIDDEN,
- )
- updated = await update_admin(body)
+ print(data)
+ if not admin.user == g.wallet.user:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
+ updated = await update_admin(user=g.wallet.user, **data.dict())
print(updated)
- return jsonify({"status": "Success"}), HTTPStatus.OK
\ No newline at end of file
+ return {"status": "Success"}
From 23d770a07489c3d74cc0c4c4d3b4a3b4b00fc393 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:18:58 +0000
Subject: [PATCH 0024/1058] remove core admin html (renamed for now)
---
lnbits/core/templates/core/core_admin.html | 717 +++++++++++++++++++++
1 file changed, 717 insertions(+)
create mode 100644 lnbits/core/templates/core/core_admin.html
diff --git a/lnbits/core/templates/core/core_admin.html b/lnbits/core/templates/core/core_admin.html
new file mode 100644
index 00000000..835fc00a
--- /dev/null
+++ b/lnbits/core/templates/core/core_admin.html
@@ -0,0 +1,717 @@
+{% extends "public.html" %} {% from "macros.jinja" import window_vars with
+context %} {% block page %}
+
+
+
+
+
+ Welcome to LNbits
+
+
+ Fill in the information below to setup your LNbits instance. Details
+ can be changed later.
+
+
+
+
+
+
+ Branding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Service settings
+
+
+
+ Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
+ should be filled in for you
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View project in GitHub
+ Donate
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }}
+
+{% endblock %}
From 165ab6d0b50b49cad69b46100bce0f87fd6f7257 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:21:38 +0000
Subject: [PATCH 0025/1058] typo
---
.env.example | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.env.example b/.env.example
index 68e25ad1..bd189484 100644
--- a/.env.example
+++ b/.env.example
@@ -5,7 +5,7 @@ DEBUG=false
LNBITS_ADMIN_USERS="" # User IDs seperated by comma
LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS
LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
From 844e11edeb441fd2e7c7b40d11c25cc4019471bf Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:22:23 +0000
Subject: [PATCH 0026/1058] add admin_ui env
---
lnbits/settings.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/settings.py b/lnbits/settings.py
index 3f4e31cc..43cb87cb 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,5 +1,6 @@
import importlib
import subprocess
+from email.policy import default
from os import path
from typing import List
@@ -27,6 +28,7 @@ LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None)
LNBITS_ALLOWED_USERS: List[str] = [
x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str)
]
+LNBITS_ADMIN_UI = env.bool("LNBITS_ADMIN_UI", default=False)
LNBITS_ADMIN_USERS: List[str] = [
x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str)
]
From 4336613028e05dae5b6adf3b543903f6fc365a44 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:23:16 +0000
Subject: [PATCH 0027/1058] add db config at startup
---
lnbits/commands.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 0f7454f2..8c39c338 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -52,6 +52,25 @@ def bundle_vendored():
with open(outputpath, "w") as f:
f.write(output)
+async def get_admin_settings():
+ from lnbits.extensions.admin.models import Admin
+
+ async with core_db.connect() as conn:
+
+ if conn.type == SQLITE:
+ exists = await conn.fetchone(
+ "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
+ )
+ elif conn.type in {POSTGRES, COCKROACH}:
+ exists = await conn.fetchone(
+ "SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
+ )
+ if not exists:
+ return False
+
+ row = await conn.fetchone("SELECT * from admin")
+
+ return Admin(**row) if row else None
async def migrate_databases():
"""Creates the necessary databases if they don't exist already; or migrates them."""
From 32a6a6ae2fb9fa3ba01c24f31067a5115feca4e3 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:23:53 +0000
Subject: [PATCH 0028/1058] get admin settings at startup
---
lnbits/app.py | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 51482538..6a66b99e 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -18,6 +18,7 @@ from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
+from .commands import get_admin_settings
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -42,6 +43,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""Create application factory.
:param config_object: The configuration object to use.
"""
+<<<<<<< HEAD
configure_logger()
app = FastAPI(
@@ -53,6 +55,14 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
+=======
+ app = FastAPI()
+
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ check_settings(app)
+
+ app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
+>>>>>>> e3a1b3ae (get admin settings at startup)
app.mount(
"/core/static",
StaticFiles(packages=[("lnbits.core", "static")]),
@@ -64,7 +74,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
app.add_middleware(
CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"]
)
-
g().config = lnbits.settings
g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}"
@@ -102,6 +111,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
+def check_settings(app: FastAPI):
+ @app.on_event("startup")
+ async def check_settings_admin():
+ while True:
+ admin_set = await get_admin_settings()
+ if admin_set :
+ break
+ print("ERROR:", admin_set)
+ await asyncio.sleep(5)
+ # admin_set = await get_admin_settings()
+ g().admin_conf = admin_set
+
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
async def check_wallet_status():
From f245f3188b7f35b6f9388e9caa12d3d6a0b20c3e Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:24:11 +0000
Subject: [PATCH 0029/1058] remove core admin.html
---
lnbits/core/templates/core/admin.html | 717 --------------------------
1 file changed, 717 deletions(-)
delete mode 100644 lnbits/core/templates/core/admin.html
diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html
deleted file mode 100644
index e8176555..00000000
--- a/lnbits/core/templates/core/admin.html
+++ /dev/null
@@ -1,717 +0,0 @@
-{% extends "public.html" %} {% from "macros.jinja" import window_vars with
-context %} {% block page %}
-
-
-
-
-
- Welcome to LNbits
-
-
- Fill in the information below to setup your LNbits instance. Details
- can be changed later.
-
-
-
-
-
-
- Branding
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Service settings
-
-
-
- Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
- should be filled in for you
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- View project in GitHub
- Donate
-
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(funding) }}
-
-{% endblock %}
From de21f0216161ac9f45cf17f42f5be708404156d0 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 18 Mar 2022 16:55:31 +0000
Subject: [PATCH 0030/1058] refactor ui
---
.../admin/templates/admin/index.html | 727 +++++++++++++++++-
1 file changed, 712 insertions(+), 15 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index a6b45625..65ac9f33 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1,6 +1,670 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
+
+
+
+
+
+
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+
+
+
+
+
+
+
+ Wallets Management
+
+
+
+
+
Funding Source Info
+
+ {%raw%}
+ Funding Source: {{data.admin.funding_source}}
+ Balance: {{data.admin.balance / 1000}} sats
+ {%endraw%}
+
+
+
+
+
+
+
+
+ TopUp a wallet
+
+
+
+
+
+
+
+
+
Funding Sources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+ User Management
+
+
+ Super Admin: {% raw
+ %}{{this.data.admin.user}}{% endraw %}
+
+
+
+
Admin Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
Allowed Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
+
+
+ Server Management
+
+
+
+
+
Server Info
+
+ {%raw%}
+ SQlite: {{data.admin.data_folder}}
+ Postgres: {{data.admin.database_url}}
+ {%endraw%}
+
+
+
+
+
+
+
+
Miscelaneous
+
+
+ Force HTTPS
+ Prefer secure URLs
+
+
+
+
+
+
+
+ Hide API
+ Hides wallet api, extensions can choose to honor
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+ UI Management
+
+
+
+
+
+
+
+
Default Wallet Name
+
+
+
+
+
+
+
+
+
Advertisement Slots
+
+
+
+
+ {% raw %}
+
+ {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
+
+ {% endraw %}
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+
+
+
+
Admin
-
+
@@ -426,6 +1090,7 @@
return {
wallet: {data: {}},
cancel: {},
+ tab: 'funding',
data: {
funding_source: [
'CLightningWallet',
@@ -436,24 +1101,14 @@
'LnbitsWallet',
'OpenNodeWallet'
],
-
+
admin: {
- user: '{{ user.id }}',
- site_title: '{{admin.site_title}}',
- tagline: '{{admin.site_tagline}}',
- description: '{{admin.site_description}}',
- admin_users: '{{admin.admin_users}}',
- service_fee: parseFloat('{{admin.service_fee}}'),
- default_wallet_name: '{{admin.default_wallet_name}}',
- data_folder: '{{admin.data_folder}}',
- funding_source_primary: '{{admin.funding_source}}',
- disabled_ext: '{{admin.disabled_ext}}'.split(','),
edited: [],
funding: {},
senddata: {}
}
},
-
+ themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'],
options: [
'bleskomat',
'captcha',
@@ -489,9 +1144,51 @@
for (i = 0; i < funding.length; i++) {
self.data.admin.funding[funding[i].backend_wallet] = funding[i]
}
- console.log(self.data.admin)
+ let settings = JSON.parse('{{ settings | tojson|safe }}')
+ settings.balance = '{{ balance }}'
+ this.data.admin = {...this.data.admin, ...settings}
+ console.log(this.g.user)
},
methods: {
+ addAdminUser(){
+ let addUser = this.data.admin_users_add
+ let admin_users = this.data.admin.admin_users
+ if(addUser.length && !admin_users.includes(addUser)){
+ admin_users.push(addUser)
+ this.data.admin.admin_users = admin_users
+ this.data.admin_users_add = ""
+ }
+ },
+ removeAdminUser(user){
+ let admin_users = this.data.admin.admin_users
+ this.data.admin.admin_users = admin_users.filter(u => u !== user)
+ },
+ addAllowedUser(){
+ let addUser = this.data.allowed_users_add
+ let allowed_users = this.data.admin.allowed_users
+ if(addUser.length && !allowed_users.includes(addUser)){
+ allowed_users.push(addUser)
+ this.data.admin.allowed_users = allowed_users
+ this.data.allowed_users_add = ""
+ }
+ },
+ removeAllowedUser(user){
+ let allowed_users = this.data.admin.allowed_users
+ this.data.admin.allowed_users = allowed_users.filter(u => u !== user)
+ },
+ addAdSpace(){
+ let adSpace = this.data.ad_space_add
+ let spaces = this.data.admin.ad_space
+ if(adSpace.length && !spaces.includes(adSpace)){
+ spaces.push(adSpace)
+ this.data.admin.ad_space = spaces
+ this.data.ad_space_add = ""
+ }
+ },
+ removeAdSpace(ad){
+ let spaces = this.data.admin.ad_space
+ this.data.admin.ad_space = spaces.filter(s => s !== ad)
+ },
topupWallet: function () {
var self = this
LNbits.api
From 582cc52ac61236ca7ae219b49e20d0954e983411 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 18 Mar 2022 16:59:06 +0000
Subject: [PATCH 0031/1058] make it work from g()
---
lnbits/app.py | 34 +++---
lnbits/config.py | 62 ++++++++++
lnbits/extensions/admin/crud.py | 2 +-
lnbits/extensions/admin/migrations.py | 162 +++++++++++++++++---------
lnbits/extensions/admin/models.py | 27 +++--
lnbits/extensions/admin/views.py | 8 +-
lnbits/settings.py | 2 +-
7 files changed, 218 insertions(+), 79 deletions(-)
create mode 100644 lnbits/config.py
diff --git a/lnbits/app.py b/lnbits/app.py
index 6a66b99e..ccac1e00 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -19,6 +19,7 @@ import lnbits.settings
from lnbits.core.tasks import register_task_listeners
from .commands import get_admin_settings
+from .config import WALLET, conf
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -29,7 +30,8 @@ from .helpers import (
url_for_vendored,
)
from .requestvars import g
-from .settings import WALLET
+
+# from .settings import WALLET
from .tasks import (
catch_everything_and_restart,
check_pending_payments,
@@ -43,7 +45,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""Create application factory.
:param config_object: The configuration object to use.
"""
-<<<<<<< HEAD
configure_logger()
app = FastAPI(
@@ -55,20 +56,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
-=======
- app = FastAPI()
-
- if lnbits.settings.LNBITS_ADMIN_UI:
- check_settings(app)
-
- app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
->>>>>>> e3a1b3ae (get admin settings at startup)
app.mount(
"/core/static",
StaticFiles(packages=[("lnbits.core", "static")]),
name="core_static",
)
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ g().admin_conf = conf
+ check_settings(app)
+
+ g().WALLET = WALLET
+
origins = ["*"]
app.add_middleware(
@@ -110,18 +109,27 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
-
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
+
+ def removeEmptyString(arr):
+ return list(filter(None, arr))
+
while True:
admin_set = await get_admin_settings()
if admin_set :
break
print("ERROR:", admin_set)
await asyncio.sleep(5)
- # admin_set = await get_admin_settings()
- g().admin_conf = admin_set
+
+ admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
+ admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(','))
+ admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(','))
+ admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(','))
+ admin_set.theme = removeEmptyString(admin_set.theme.split(','))
+ admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
+ g().admin_conf = conf.copy(update=admin_set.dict())
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
diff --git a/lnbits/config.py b/lnbits/config.py
new file mode 100644
index 00000000..02e8cf53
--- /dev/null
+++ b/lnbits/config.py
@@ -0,0 +1,62 @@
+import importlib
+import json
+from os import getenv, path
+from typing import List, Optional
+
+from pydantic import BaseSettings, Field, validator
+
+wallets_module = importlib.import_module("lnbits.wallets")
+wallet_class = getattr(
+ wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet")
+)
+
+WALLET = wallet_class()
+
+def list_parse_fallback(v):
+ try:
+ return json.loads(v)
+ except Exception as e:
+ return v.replace(' ','').split(',')
+
+class Settings(BaseSettings):
+ # users
+ admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
+ allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
+ admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS")
+ disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS")
+ funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS")
+ # ops
+ data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER")
+ database_url: str = Field(default=None, env="LNBITS_DATABASE_URL")
+ force_https: bool = Field(default=True, env="LNBITS_FORCE_HTTPS")
+ service_fee: float = Field(default=0, env="LNBITS_SERVICE_FEE")
+ hide_api: bool = Field(default=False, env="LNBITS_HIDE_API")
+ denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
+ # Change theme
+ site_title: str = Field(default=None, env="LNBITS_SITE_TITLE")
+ site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE")
+ site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
+ default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME")
+ theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
+ ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
+ # .env
+ env: Optional[str]
+ debug: Optional[str]
+ host: Optional[str]
+ port: Optional[str]
+ lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
+
+ # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True)
+ # def validate(cls, val):
+ # print(val)
+ # return val.split(',')
+
+ class Config:
+ env_file = ".env"
+ env_file_encoding = "utf-8"
+ case_sensitive = False
+ json_loads = list_parse_fallback
+
+
+conf = Settings()
+WALLET = wallet_class()
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 872d6c97..6fccb8ee 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -40,7 +40,7 @@ async def update_admin(user: str, **kwargs) -> Admin:
# new_settings = await get_admin()
# return new_settings
-async def get_admin() -> List[Admin]:
+async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 13b76923..574f772d 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -2,93 +2,151 @@ from os import getenv
from sqlalchemy.exc import OperationalError # type: ignore
+from lnbits.config import conf
from lnbits.helpers import urlsafe_short_hash
async def m001_create_admin_table(db):
- user = None
- site_title = None
- site_tagline = None
- site_description = None
- allowed_users = None
- admin_users = None
- default_wallet_name = None
- data_folder = None
- disabled_ext = None
- force_https = True
- service_fee = 0
- funding_source = ""
+ # users/server
+ user = conf.admin_users[0]
+ admin_users = ",".join(conf.admin_users)
+ allowed_users = ",".join(conf.allowed_users)
+ admin_ext = ",".join(conf.admin_ext)
+ disabled_ext = ",".join(conf.disabled_ext)
+ funding_source = conf.funding_source
+ #operational
+ data_folder = conf.data_folder
+ database_url = conf.database_url
+ force_https = conf.force_https
+ service_fee = conf.service_fee
+ hide_api = conf.hide_api
+ denomination = conf.denomination
+ # Theme'ing
+ site_title = conf.site_title
+ site_tagline = conf.site_tagline
+ site_description = conf.site_description
+ default_wallet_name = conf.default_wallet_name
+ theme = ",".join(conf.theme)
+ ad_space = ",".join(conf.ad_space)
- if getenv("LNBITS_SITE_TITLE"):
- site_title = getenv("LNBITS_SITE_TITLE")
+ # if getenv("LNBITS_ADMIN_EXTENSIONS"):
+ # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS")
- if getenv("LNBITS_SITE_TAGLINE"):
- site_tagline = getenv("LNBITS_SITE_TAGLINE")
+ # if getenv("LNBITS_DATABASE_URL"):
+ # database_url = getenv("LNBITS_DATABASE_URL")
- if getenv("LNBITS_SITE_DESCRIPTION"):
- site_description = getenv("LNBITS_SITE_DESCRIPTION")
+ # if getenv("LNBITS_HIDE_API"):
+ # hide_api = getenv("LNBITS_HIDE_API")
- if getenv("LNBITS_ALLOWED_USERS"):
- allowed_users = getenv("LNBITS_ALLOWED_USERS")
+ # if getenv("LNBITS_THEME_OPTIONS"):
+ # theme = getenv("LNBITS_THEME_OPTIONS")
- if getenv("LNBITS_ADMIN_USERS"):
- admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
- user = admin_users.split(',')[0]
+ # if getenv("LNBITS_AD_SPACE"):
+ # ad_space = getenv("LNBITS_AD_SPACE")
- if getenv("LNBITS_DEFAULT_WALLET_NAME"):
- default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
+ # if getenv("LNBITS_SITE_TITLE"):
+ # site_title = getenv("LNBITS_SITE_TITLE")
- if getenv("LNBITS_DATA_FOLDER"):
- data_folder = getenv("LNBITS_DATA_FOLDER")
+ # if getenv("LNBITS_SITE_TAGLINE"):
+ # site_tagline = getenv("LNBITS_SITE_TAGLINE")
- if getenv("LNBITS_DISABLED_EXTENSIONS"):
- disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+ # if getenv("LNBITS_SITE_DESCRIPTION"):
+ # site_description = getenv("LNBITS_SITE_DESCRIPTION")
- if getenv("LNBITS_FORCE_HTTPS"):
- force_https = getenv("LNBITS_FORCE_HTTPS")
+ # if getenv("LNBITS_ALLOWED_USERS"):
+ # allowed_users = getenv("LNBITS_ALLOWED_USERS")
- if getenv("LNBITS_SERVICE_FEE"):
- service_fee = getenv("LNBITS_SERVICE_FEE")
+ # if getenv("LNBITS_ADMIN_USERS"):
+ # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
+ # user = admin_users.split(',')[0]
+
+ # if getenv("LNBITS_DEFAULT_WALLET_NAME"):
+ # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
- if getenv("LNBITS_BACKEND_WALLET_CLASS"):
- funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
+ # if getenv("LNBITS_DATA_FOLDER"):
+ # data_folder = getenv("LNBITS_DATA_FOLDER")
+
+ # if getenv("LNBITS_DISABLED_EXTENSIONS"):
+ # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+
+ # if getenv("LNBITS_FORCE_HTTPS"):
+ # force_https = getenv("LNBITS_FORCE_HTTPS")
+
+ # if getenv("LNBITS_SERVICE_FEE"):
+ # service_fee = getenv("LNBITS_SERVICE_FEE")
+
+ # if getenv("LNBITS_DENOMINATION"):
+ # denomination = getenv("LNBITS_DENOMINATION", "sats")
+
+ # if getenv("LNBITS_BACKEND_WALLET_CLASS"):
+ # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
- "user" TEXT,
+ "user" TEXT PRIMARY KEY,
+ admin_users TEXT,
+ allowed_users TEXT,
+ admin_ext TEXT,
+ disabled_ext TEXT,
+ funding_source TEXT,
+ data_folder TEXT,
+ database_url TEXT,
+ force_https BOOLEAN,
+ service_fee REAL,
+ hide_api BOOLEAN,
+ denomination TEXT,
site_title TEXT,
site_tagline TEXT,
site_description TEXT,
- admin_users TEXT,
- allowed_users TEXT,
default_wallet_name TEXT,
- data_folder TEXT,
- disabled_ext TEXT,
- force_https BOOLEAN,
- service_fee REAL,
- funding_source TEXT
+ theme TEXT,
+ ad_space TEXT
);
"""
)
await db.execute(
"""
- INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- user.strip(),
+ INSERT INTO admin (
+ "user",
+ admin_users,
+ allowed_users,
+ admin_ext,
+ disabled_ext,
+ funding_source,
+ data_folder,
+ database_url,
+ force_https,
+ service_fee,
+ hide_api,
+ denomination,
site_title,
site_tagline,
site_description,
- admin_users[1:],
- allowed_users,
default_wallet_name,
- data_folder,
+ theme,
+ ad_space)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ user,
+ admin_users,
+ allowed_users,
+ admin_ext,
disabled_ext,
+ funding_source,
+ data_folder,
+ database_url,
force_https,
service_fee,
- funding_source,
+ hide_api,
+ denomination,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ theme,
+ ad_space,
),
)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 4080ff01..f7c64de5 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -2,7 +2,7 @@ from sqlite3 import Row
from typing import List, Optional
from fastapi import Query
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
class UpdateAdminSettings(BaseModel):
@@ -19,18 +19,27 @@ class UpdateAdminSettings(BaseModel):
funding_source: Optional[str]
class Admin(BaseModel):
+ # users
user: str
+ admin_users: Optional[str]
+ allowed_users: Optional[str]
+ admin_ext: Optional[str]
+ disabled_ext: Optional[str]
+ funding_source: Optional[str]
+ # ops
+ data_folder: Optional[str]
+ database_url: Optional[str]
+ force_https: bool = Field(default=True)
+ service_fee: float = Field(default=0)
+ hide_api: bool = Field(default=False)
+ # Change theme
site_title: Optional[str]
site_tagline: Optional[str]
site_description: Optional[str]
- allowed_users: Optional[str]
- admin_users: str
- default_wallet_name: str
- data_folder: str
- disabled_ext: str
- force_https: Optional[bool] = Query(True)
- service_fee: float
- funding_source: str
+ default_wallet_name: Optional[str]
+ denomination: str = Field(default="sats")
+ theme: Optional[str]
+ ad_space: Optional[str]
@classmethod
def from_row(cls, row: Row) -> "Admin":
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 00a0c99f..105f05a1 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -19,15 +19,17 @@ templates = Jinja2Templates(directory="templates")
@admin_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
- print(g())
funding = [f.dict() for f in await get_funding()]
-
+ error, balance = await g().WALLET.status()
print("ADMIN", admin.dict())
+ print(g().admin_conf)
return admin_renderer().TemplateResponse(
"admin/index.html", {
"request": request,
"user": user.dict(),
"admin": admin.dict(),
- "funding": funding
+ "funding": funding,
+ "settings": g().admin_conf.dict(),
+ "balance": balance
}
)
diff --git a/lnbits/settings.py b/lnbits/settings.py
index 43cb87cb..ed5c77f7 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -4,7 +4,7 @@ from email.policy import default
from os import path
from typing import List
-from environs import Env # type: ignore
+from environs import Env
env = Env()
env.read_env()
From 66a7f53b976ae98a1e18cff8305da1299e524b69 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:28:07 +0000
Subject: [PATCH 0032/1058] topup wallet endpoint
---
lnbits/extensions/admin/crud.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 6fccb8ee..683558f9 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,26 +1,31 @@
+import json
from typing import List, Optional
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
from lnbits.settings import *
+from lnbits.tasks import internal_invoice_queue
from . import db
from .models import Admin, Funding
-def update_wallet_balance(wallet_id: str, amount: int) -> str:
+async def update_wallet_balance(wallet_id: str, amount: int) -> str:
temp_id = f"temp_{urlsafe_short_hash()}"
internal_id = f"internal_{urlsafe_short_hash()}"
- create_payment(
+
+ payment = await create_payment(
wallet_id=wallet_id,
checking_id=internal_id,
payment_request="admin_internal",
payment_hash="admin_internal",
- amount=amount * 1000,
+ amount=amount*1000,
memo="Admin top up",
pending=False,
)
- return "success"
+ # manually send this for now
+ await internal_invoice_queue.put(internal_id)
+ return payment
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
From 663c7ebd2f52c4cc0ea57839d921e3730d4decef Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:28:44 +0000
Subject: [PATCH 0033/1058] update admin settings in db
---
lnbits/extensions/admin/models.py | 29 +++++++++++++++++-----------
lnbits/extensions/admin/views_api.py | 8 ++++----
2 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index f7c64de5..36d9b815 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -6,17 +6,24 @@ from pydantic import BaseModel, Field
class UpdateAdminSettings(BaseModel):
- site_title: Optional[str]
- site_tagline: Optional[str]
- site_description: Optional[str]
- allowed_users: Optional[str]
- admin_users: Optional[str]
- default_wallet_name: Optional[str]
- data_folder: Optional[str]
- disabled_ext: Optional[str]
- force_https: Optional[bool]
- service_fee: Optional[float]
- funding_source: Optional[str]
+ # users
+ admin_users: str = Query(None)
+ allowed_users: str = Query(None)
+ admin_ext: str = Query(None)
+ disabled_ext: str = Query(None)
+ funding_source: str = Query(None)
+ # ops
+ force_https: bool = Query(None)
+ service_fee: float = Query(None, ge=0)
+ hide_api: bool = Query(None)
+ # Change theme
+ site_title: str = Query(None)
+ site_tagline: str = Query(None)
+ site_description: str = Query(None)
+ default_wallet_name: str = Query(None)
+ denomination: str = Query(None)
+ theme: str = Query(None)
+ ad_space: str = Query(None)
class Admin(BaseModel):
# users
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index b2c65be2..cb526aa5 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -12,16 +12,16 @@ from .crud import get_admin, update_admin, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
-async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)):
- print(g.wallet)
+async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)):
try:
wallet = await get_wallet(wallet_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
- print(wallet)
- print(topup_amount)
+
+ await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
+
return {"status": "Success"}
From bc090190fca9a170566e1d605bdbdbd3536c70f5 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:29:18 +0000
Subject: [PATCH 0034/1058] update settings and topup logic
---
.../admin/templates/admin/index.html | 91 ++++++++++++-------
1 file changed, 57 insertions(+), 34 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 65ac9f33..e9ddc7c4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -30,7 +30,7 @@
-
+
@@ -61,7 +61,7 @@
@@ -577,7 +577,6 @@
Site Description
s !== ad)
},
- topupWallet: function () {
- var self = this
+ topupWallet() {
LNbits.api
.request(
'GET',
'/admin/api/v1/admin/' +
- self.wallet.id +
+ this.wallet.data.id +
'/' +
- self.wallet.data.amount,
- self.g.user.wallets[0].adminkey
+ this.wallet.data.amount,
+ this.g.user.wallets[0].adminkey
)
- .then(function (response) {
- self.$q.notify({
+ .then((response) => {
+ this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- self.wallet.amount +
- ' to ' +
- self.wallet.id,
+ 'Success! Added ' +
+ this.wallet.data.amount +
+ ' to ' +
+ this.wallet.data.id,
icon: null
})
+ this.wallet.data = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -1224,36 +1224,59 @@
self.data.admin.edited.push(source)
console.log(self.data.admin.edited)
},
- UpdateLNbits: function () {
- var self = this
- let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin
+ UpdateLNbits() {
+ let {
+ admin_users,
+ allowed_users,
+ admin_ext,
+ disabled_ext,
+ funding_source,
+ force_https,
+ service_fee,
+ hide_api,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ denomination,
+ theme,
+ ad_space
+ } = this.data.admin
+ //console.log("this", this.data.admin)
let data = {
- site_title,
- site_tagline: this.data.admin.tagline,
- site_description: this.data.admin.description,
- admin_users: admin_users.toString(),
- default_wallet_name,
- data_folder,
+ admin_users: admin_users.toString(),
+ allowed_users: allowed_users.toString(),
+ admin_ext: admin_ext.toString(),
disabled_ext: disabled_ext.toString(),
- service_fee,
- funding_source: funding_source_primary}
+ funding_source,
+ force_https,
+ service_fee,
+ hide_api,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ denomination,
+ theme: theme.toString(),
+ ad_space: ad_space.toString()
+ }
console.log(data)
LNbits.api
.request(
'POST',
'/admin/api/v1/admin/',
- self.g.user.wallets[0].adminkey,
+ this.g.user.wallets[0].adminkey,
data
)
- .then(function (response) {
+ .then(response => {
console.log(response.data)
- self.$q.notify({
+ this.$q.notify({
type: 'positive',
message:
'Success! Added ' +
- self.wallet.amount +
+ this.wallet.amount +
' to ' +
- self.wallet.id,
+ this.wallet.id,
icon: null
})
})
From 313574df1991c47b6a0dbc390f8d839278e200d4 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:34:47 +0000
Subject: [PATCH 0035/1058] make removeEmptyString fn as helper fn
---
lnbits/app.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index ccac1e00..5df439dc 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -26,6 +26,7 @@ from .helpers import (
get_css_vendored,
get_js_vendored,
get_valid_extensions,
+ removeEmptyString,
template_renderer,
url_for_vendored,
)
@@ -113,9 +114,6 @@ def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
- def removeEmptyString(arr):
- return list(filter(None, arr))
-
while True:
admin_set = await get_admin_settings()
if admin_set :
From edfa98f00e7111096321d259fca4cd2eb36ac3d5 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:35:15 +0000
Subject: [PATCH 0036/1058] add some defaults
---
lnbits/config.py | 6 +++---
lnbits/extensions/admin/models.py | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 02e8cf53..b2fbfff1 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -33,10 +33,10 @@ class Settings(BaseSettings):
hide_api: bool = Field(default=False, env="LNBITS_HIDE_API")
denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
# Change theme
- site_title: str = Field(default=None, env="LNBITS_SITE_TITLE")
- site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE")
+ site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE")
+ site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
- default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME")
+ default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 36d9b815..0f25679d 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -17,11 +17,11 @@ class UpdateAdminSettings(BaseModel):
service_fee: float = Query(None, ge=0)
hide_api: bool = Query(None)
# Change theme
- site_title: str = Query(None)
- site_tagline: str = Query(None)
+ site_title: str = Query("LNbits")
+ site_tagline: str = Query("free and open-source lightning wallet")
site_description: str = Query(None)
- default_wallet_name: str = Query(None)
- denomination: str = Query(None)
+ default_wallet_name: str = Query("LNbits wallet")
+ denomination: str = Query("sats")
theme: str = Query(None)
ad_space: str = Query(None)
From ba6bda39ab4a6c50631658d9bee85329c4784950 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:36:04 +0000
Subject: [PATCH 0037/1058] removeEmtpy sting as helper fn
---
lnbits/helpers.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e213240c..e456f715 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -154,8 +154,20 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s
url = f"{base}{endpoint}{url_params}"
return url
+def removeEmptyString(arr):
+ return list(filter(None, arr))
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
+ if(settings.LNBITS_ADMIN_UI):
+ _ = g().admin_conf
+ settings.LNBITS_AD_SPACE = _.ad_space
+ settings.LNBITS_HIDE_API = _.hide_api
+ settings.LNBITS_SITE_TITLE = _.site_title
+ settings.LNBITS_DENOMINATION = _.denomination
+ settings.LNBITS_SITE_TAGLINE = _.site_tagline
+ settings.LNBITS_SITE_DESCRIPTION = _.site_description
+ settings.LNBITS_THEME_OPTIONS = _.theme
+
t = Jinja2Templates(
loader=jinja2.FileSystemLoader(
["lnbits/templates", "lnbits/core/templates", *additional_folders]
From 0a211a2fb2d1fdf7c135cf637b768c8fc2855a64 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:41:11 +0000
Subject: [PATCH 0038/1058] cleanup
---
lnbits/extensions/admin/crud.py | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 683558f9..e14ad194 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,9 +1,7 @@
-import json
-from typing import List, Optional
+from typing import List
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
-from lnbits.settings import *
from lnbits.tasks import internal_invoice_queue
from . import db
@@ -37,14 +35,6 @@ async def update_admin(user: str, **kwargs) -> Admin:
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
-# async def update_admin(user: str, **kwargs) -> Optional[Admin]:
-# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
-# await db.execute(
-# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user)
-# )
-# new_settings = await get_admin()
-# return new_settings
-
async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
From 5ec7f21650897699f39723bbe93edd3d53da1fd2 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:42:28 +0000
Subject: [PATCH 0039/1058] make string to list
---
lnbits/extensions/admin/views_api.py | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index cb526aa5..1d4e6a9c 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,5 +1,6 @@
from http import HTTPStatus
+# from config import conf
from fastapi import Body, Depends, Request
from starlette.exceptions import HTTPException
@@ -7,6 +8,8 @@ from lnbits.core.crud import get_wallet
from lnbits.decorators import WalletTypeInfo, require_admin_key
from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+from lnbits.helpers import removeEmptyString
+from lnbits.requestvars import g
from .crud import get_admin, update_admin, update_wallet_balance
@@ -19,7 +22,7 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
-
+
await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
return {"status": "Success"}
@@ -29,14 +32,24 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
async def api_update_admin(
request: Request,
data: UpdateAdminSettings = Body(...),
- g: WalletTypeInfo = Depends(require_admin_key)
+ w: WalletTypeInfo = Depends(require_admin_key)
):
admin = await get_admin()
print(data)
- if not admin.user == g.wallet.user:
+ if not admin.user == w.wallet.user:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
- updated = await update_admin(user=g.wallet.user, **data.dict())
- print(updated)
+ updated = await update_admin(user=w.wallet.user, **data.dict())
+
+ updated.admin_users = removeEmptyString(updated.admin_users.split(','))
+ updated.allowed_users = removeEmptyString(updated.allowed_users.split(','))
+ updated.admin_ext = removeEmptyString(updated.admin_ext.split(','))
+ updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(','))
+ updated.theme = removeEmptyString(updated.theme.split(','))
+ updated.ad_space = removeEmptyString(updated.ad_space.split(','))
+
+ g().admin_conf = g().admin_conf.copy(update=updated.dict())
+
+ print(g().admin_conf)
return {"status": "Success"}
From 4d16c296aa1d8b8375e3f131e459a2f2f80f0d3b Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:42:47 +0000
Subject: [PATCH 0040/1058] success message
---
lnbits/extensions/admin/templates/admin/index.html | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index e9ddc7c4..9aa4f12a 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1273,10 +1273,7 @@
this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- this.wallet.amount +
- ' to ' +
- this.wallet.id,
+ 'Success! Settings changed!',
icon: null
})
})
From 2c48e3aa5f1e561e4106c60bfe0e4266a86711b3 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 14 Apr 2022 10:42:26 +0100
Subject: [PATCH 0041/1058] allow html to be passed to description
---
lnbits/core/templates/core/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index f769b44f..03cf706f 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -82,7 +82,7 @@
>
-
{{SITE_DESCRIPTION}}
+
{{SITE_DESCRIPTION | safe}}
From f16ead4f7303787d945ab9dc39550ae3bc0ec611 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 14 Apr 2022 16:37:13 +0100
Subject: [PATCH 0042/1058] update funding wallets
---
lnbits/extensions/admin/crud.py | 12 +
.../admin/templates/admin/index.html | 523 ++++++++++--------
lnbits/extensions/admin/views.py | 3 +-
lnbits/extensions/admin/views_api.py | 19 +-
4 files changed, 315 insertions(+), 242 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index e14ad194..dd39e8e4 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -39,6 +39,18 @@ async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
+async def update_funding(data: Funding) -> Funding:
+ await db.execute(
+ """
+ UPDATE funding
+ SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
+ WHERE id = ?
+ """,
+ (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
+ )
+ row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,))
+ assert row, "Newly updated settings couldn't be retrieved"
+ return Funding(**row) if row else None
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM funding")
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 9aa4f12a..d56b3d79 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -31,11 +31,11 @@
-
-
-
- Wallets Management
-
+
+
+
+ Wallets Management
+
@@ -62,43 +62,96 @@
-
TopUp a wallet
-
-
-
-
-
-
-
-
+
TopUp a wallet
+
Funding Sources
-
+ {% raw %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+ User Management
+
+
+ Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %}
+
+
Admin Users
+ hint="Users with admin privileges"
+ >
{% raw %}
-
-
-
- {% raw %}
-
Allowed Users
+
- {{ user }}
-
- {% endraw %}
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
+
+
+ Server Management
-
-
-
-
-
Disabled Extensions
-
-
-
-
-
- Save
-
-
-
-
-
- Server Management
-
Server Info
{%raw%}
- SQlite: {{data.admin.data_folder}}
- Postgres: {{data.admin.database_url}}
+
+ SQlite: {{data.admin.data_folder}}
+
+
+ Postgres: {{data.admin.database_url}}
+
{%endraw%}
@@ -520,7 +576,10 @@
Hide API
- Hides wallet api, extensions can choose to honor
+ Hides wallet api, extensions can choose to
+ honor
-
-
-
- Save
-
-
-
-
-
- UI Management
-
+
+
+
+ Save
+
+
+
+
+
+ UI Management
+
+
Default Wallet Name
@@ -628,12 +682,15 @@
@keydown.enter="addAdSpace"
type="text"
label="Ad image URL"
- hint="Ad image filepaths or urls, extensions can choose to honor">
+ hint="Ad image filepaths or urls, extensions can choose to honor"
+ >
{% raw %}
-
-
-
-
- Save
-
-
-
-
-
+
+
+
+ Save
+
+
+
+
+
-
-
-Admin
-
+
+
-
-
-
-
- Wallet topup
-
-
-
-
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }}
@@ -1100,14 +1114,22 @@
'LnbitsWallet',
'OpenNodeWallet'
],
-
+
admin: {
edited: [],
- funding: {},
+ funding: [],
senddata: {}
}
},
- themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'],
+ themes: [
+ 'classic',
+ 'bitcoin',
+ 'flamingo',
+ 'mint',
+ 'autumn',
+ 'monochrome',
+ 'salvador'
+ ],
options: [
'bleskomat',
'captcha',
@@ -1139,10 +1161,13 @@
self.cancel.on = true
}
funding = JSON.parse(String('{{ funding | tojson|safe }}'))
- var i
+ funding.map(f => {
+ this.data.admin.funding.push(f)
+ })
+ /*var i
for (i = 0; i < funding.length; i++) {
self.data.admin.funding[funding[i].backend_wallet] = funding[i]
- }
+ }*/
let settings = JSON.parse('{{ settings | tojson|safe }}')
settings.balance = '{{ balance }}'
this.data.admin = {...this.data.admin, ...settings}
@@ -1150,42 +1175,42 @@
console.log(settings)
},
methods: {
- addAdminUser(){
+ addAdminUser() {
let addUser = this.data.admin_users_add
let admin_users = this.data.admin.admin_users
- if(addUser.length && !admin_users.includes(addUser)){
+ if (addUser.length && !admin_users.includes(addUser)) {
admin_users.push(addUser)
this.data.admin.admin_users = admin_users
- this.data.admin_users_add = ""
+ this.data.admin_users_add = ''
}
},
- removeAdminUser(user){
+ removeAdminUser(user) {
let admin_users = this.data.admin.admin_users
this.data.admin.admin_users = admin_users.filter(u => u !== user)
},
- addAllowedUser(){
+ addAllowedUser() {
let addUser = this.data.allowed_users_add
let allowed_users = this.data.admin.allowed_users
- if(addUser.length && !allowed_users.includes(addUser)){
+ if (addUser.length && !allowed_users.includes(addUser)) {
allowed_users.push(addUser)
this.data.admin.allowed_users = allowed_users
- this.data.allowed_users_add = ""
+ this.data.allowed_users_add = ''
}
},
- removeAllowedUser(user){
+ removeAllowedUser(user) {
let allowed_users = this.data.admin.allowed_users
this.data.admin.allowed_users = allowed_users.filter(u => u !== user)
},
- addAdSpace(){
+ addAdSpace() {
let adSpace = this.data.ad_space_add
let spaces = this.data.admin.ad_space
- if(adSpace.length && !spaces.includes(adSpace)){
+ if (adSpace.length && !spaces.includes(adSpace)) {
spaces.push(adSpace)
this.data.admin.ad_space = spaces
- this.data.ad_space_add = ""
+ this.data.ad_space_add = ''
}
},
- removeAdSpace(ad){
+ removeAdSpace(ad) {
let spaces = this.data.admin.ad_space
this.data.admin.ad_space = spaces.filter(s => s !== ad)
},
@@ -1199,14 +1224,14 @@
this.wallet.data.amount,
this.g.user.wallets[0].adminkey
)
- .then((response) => {
+ .then(response => {
this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- this.wallet.data.amount +
- ' to ' +
- this.wallet.data.id,
+ 'Success! Added ' +
+ this.wallet.data.amount +
+ ' to ' +
+ this.wallet.data.id,
icon: null
})
this.wallet.data = {}
@@ -1224,6 +1249,29 @@
self.data.admin.edited.push(source)
console.log(self.data.admin.edited)
},
+ updateFunding(fund) {
+ let data = this.data.admin.funding.find(v => v.backend_wallet == fund)
+
+ LNbits.api
+ .request(
+ 'POST',
+ '/admin/api/v1/admin/funding',
+ this.g.user.wallets[0].adminkey,
+ data
+ )
+ .then(response => {
+ //let wallet = response.data.backend_wallet
+ //this.data.admin.funding[wallet] = response.data
+ //this.data.admin.funding[wallet].endpoint = response.data.endpoint
+ //console.log(this.data.admin.funding)
+ //console.log(this.data.admin)
+ this.$q.notify({
+ type: 'positive',
+ message: `Success! ${response.data.backend_wallet} changed!`,
+ icon: null
+ })
+ })
+ },
UpdateLNbits() {
let {
admin_users,
@@ -1272,8 +1320,7 @@
console.log(response.data)
this.$q.notify({
type: 'positive',
- message:
- 'Success! Settings changed!',
+ message: 'Success! Settings changed!',
icon: null
})
})
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 105f05a1..24b8ca85 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -21,8 +21,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
funding = [f.dict() for f in await get_funding()]
error, balance = await g().WALLET.status()
- print("ADMIN", admin.dict())
- print(g().admin_conf)
+
return admin_renderer().TemplateResponse(
"admin/index.html", {
"request": request,
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 1d4e6a9c..b797dc2d 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -7,11 +7,11 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_wallet
from lnbits.decorators import WalletTypeInfo, require_admin_key
from lnbits.extensions.admin import admin_ext
-from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+from lnbits.extensions.admin.models import Admin, Funding, UpdateAdminSettings
from lnbits.helpers import removeEmptyString
from lnbits.requestvars import g
-from .crud import get_admin, update_admin, update_wallet_balance
+from .crud import get_admin, update_admin, update_funding, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
@@ -53,3 +53,18 @@ async def api_update_admin(
print(g().admin_conf)
return {"status": "Success"}
+
+@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
+async def api_update_funding(
+ request: Request,
+ data: Funding = Body(...),
+ w: WalletTypeInfo = Depends(require_admin_key)
+ ):
+ admin = await get_admin()
+
+ if not admin.user == w.wallet.user:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
+ funding = await update_funding(data=data)
+ return funding
From 5a3ad81c315f42a7b482714e943a1b0a72028e93 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 18 Apr 2022 14:25:06 +0100
Subject: [PATCH 0043/1058] allow user settings without restart
---
lnbits/core/views/generic.py | 7 ++++++-
lnbits/decorators.py | 8 ++++++++
lnbits/helpers.py | 3 +++
3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 31a7b030..83648c44 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -15,7 +15,9 @@ from lnbits.core import db
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.helpers import template_renderer, url_for
+from lnbits.requestvars import g
from lnbits.settings import (
+ LNBITS_ADMIN_UI,
LNBITS_ADMIN_USERS,
LNBITS_ALLOWED_USERS,
LNBITS_CUSTOM_LOGO,
@@ -37,7 +39,6 @@ from ..services import pay_invoice, redeem_lnurl_withdraw
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
-
@core_html_routes.get("/favicon.ico", response_class=FileResponse)
async def favicon():
return FileResponse("lnbits/core/static/favicon.ico")
@@ -119,6 +120,10 @@ async def wallet(
wallet_name = nme
service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+
if not user_id:
user = await get_user((await create_account()).id)
logger.info(f"Create user {user.id}") # type: ignore
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index d4aa63ae..f951163f 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -16,6 +16,7 @@ from lnbits.core.models import User, Wallet
from lnbits.requestvars import g
from lnbits.settings import (
LNBITS_ADMIN_EXTENSIONS,
+ LNBITS_ADMIN_UI,
LNBITS_ADMIN_USERS,
LNBITS_ALLOWED_USERS,
)
@@ -138,6 +139,9 @@ async def get_key_type(
detail="Invoice (or Admin) key required.",
)
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+
for typenr, WalletChecker in zip(
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
):
@@ -231,6 +235,10 @@ async def check_user_exists(usr: UUID4) -> User:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
+
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
raise HTTPException(
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e456f715..1167143f 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -24,6 +24,9 @@ class Extension(NamedTuple):
class ExtensionManager:
def __init__(self):
+ if settings.LNBITS_ADMIN_UI:
+ settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext
+ settings.LNBITS_ADMIN_EXTENSIONS = g().admin_conf.admin_ext
self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS
self._admin_only: List[str] = [
x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS
From 1ff8a9fce5c15d421a8c37a3f8bc908a4b249510 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 18 Apr 2022 14:25:26 +0100
Subject: [PATCH 0044/1058] advert for server restart option
---
lnbits/extensions/admin/templates/admin/index.html | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d56b3d79..089c5f1c 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -51,7 +51,9 @@
-
Active Funding
+
+ Active Funding (Requires server restart)
+
Date: Thu, 21 Apr 2022 11:08:26 +0100
Subject: [PATCH 0045/1058] cleanup prints and console logs
---
lnbits/extensions/admin/crud.py | 2 +-
lnbits/extensions/admin/templates/admin/index.html | 4 ++--
lnbits/extensions/admin/views_api.py | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index dd39e8e4..f866bc1a 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -27,7 +27,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
- print("UPDATE", q)
+ # print("UPDATE", q)
await db.execute(
f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 089c5f1c..584d3a33 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1310,7 +1310,7 @@
theme: theme.toString(),
ad_space: ad_space.toString()
}
- console.log(data)
+ //console.log(data)
LNbits.api
.request(
'POST',
@@ -1319,7 +1319,7 @@
data
)
.then(response => {
- console.log(response.data)
+ //console.log(response.data)
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index b797dc2d..c0650c8a 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -35,7 +35,7 @@ async def api_update_admin(
w: WalletTypeInfo = Depends(require_admin_key)
):
admin = await get_admin()
- print(data)
+ # print(data)
if not admin.user == w.wallet.user:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
@@ -51,7 +51,7 @@ async def api_update_admin(
g().admin_conf = g().admin_conf.copy(update=updated.dict())
- print(g().admin_conf)
+ # print(g().admin_conf)
return {"status": "Success"}
@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
From b0f9c82e1b0b16e3e1d499f5173a0368e5d9c93e Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 10:49:21 +0100
Subject: [PATCH 0046/1058] create first user on fresh install
---
.env.example | 4 ++--
lnbits/config.py | 3 ++-
lnbits/extensions/admin/README.md | 15 ++++++++-------
lnbits/extensions/admin/migrations.py | 15 ++++++++++++++-
4 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/.env.example b/.env.example
index bd189484..7a49d5c5 100644
--- a/.env.example
+++ b/.env.example
@@ -4,8 +4,8 @@ PORT=5000
DEBUG=false
LNBITS_ADMIN_USERS="" # User IDs seperated by comma
-LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS
+LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
diff --git a/lnbits/config.py b/lnbits/config.py
index b2fbfff1..3ce51c3c 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -19,6 +19,7 @@ def list_parse_fallback(v):
return v.replace(' ','').split(',')
class Settings(BaseSettings):
+ admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI")
# users
admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
@@ -37,7 +38,7 @@ class Settings(BaseSettings):
site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
- theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
+ theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
env: Optional[str]
diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md
index 27729459..6cf073a1 100644
--- a/lnbits/extensions/admin/README.md
+++ b/lnbits/extensions/admin/README.md
@@ -1,11 +1,12 @@
-Example Extension
-*tagline*
-This is an example extension to help you organise and build you own.
+# Admin Extension
-Try to include an image
-
+## Dashboard to manage LNbits from the UI
+With AdminUI you can manage your LNbits from the UI
-If your extension has API endpoints, include useful ones here
+
-curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"
+## Before you start
+
+**This extension doesn't discard the need for the `.env` file!**
+In the .env file, set the `LNBITS_ADMIN_USERS` variable to include at least your user id.
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 574f772d..0e22e667 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -6,9 +6,22 @@ from lnbits.config import conf
from lnbits.helpers import urlsafe_short_hash
+async def get_admin_user():
+ if(conf.admin_users[0]):
+ return conf.admin_users[0]
+ from lnbits.core.crud import create_account, get_user
+ print("Seems like there's no admin users yet. Let's create an account for you!")
+ account = await create_account()
+ user = account.id
+ assert user, "Newly created user couldn't be retrieved"
+ print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ return user
+
+
+
async def m001_create_admin_table(db):
# users/server
- user = conf.admin_users[0]
+ user = await get_admin_user()
admin_users = ",".join(conf.admin_users)
allowed_users = ",".join(conf.allowed_users)
admin_ext = ",".join(conf.admin_ext)
From 2f2d70f9a8cdd3edc59cb3c71b841b6823309dcf Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 12:29:58 +0100
Subject: [PATCH 0047/1058] fix schemas for admin
---
lnbits/commands.py | 11 ++++++++---
lnbits/extensions/admin/crud.py | 12 ++++++------
lnbits/extensions/admin/migrations.py | 26 +++++++++++++-------------
3 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 8c39c338..7d9b49e2 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -55,8 +55,12 @@ def bundle_vendored():
async def get_admin_settings():
from lnbits.extensions.admin.models import Admin
- async with core_db.connect() as conn:
+ try:
+ ext_db = importlib.import_module(f"lnbits.extensions.admin").db
+ except:
+ return False
+ async with ext_db.connect() as conn:
if conn.type == SQLITE:
exists = await conn.fetchone(
"SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
@@ -65,11 +69,12 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
+ print("EXISTS", exists)
if not exists:
return False
- row = await conn.fetchone("SELECT * from admin")
-
+ row = await conn.fetchone("SELECT * from admin.admin")
+
return Admin(**row) if row else None
async def migrate_databases():
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index f866bc1a..67fbc614 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -29,30 +29,30 @@ async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
await db.execute(
- f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
+ f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,))
+ row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,))
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
async def get_admin() -> Admin:
- row = await db.fetchone("SELECT * FROM admin")
+ row = await db.fetchone("SELECT * FROM admin.admin")
return Admin(**row) if row else None
async def update_funding(data: Funding) -> Funding:
await db.execute(
"""
- UPDATE funding
+ UPDATE admin.funding
SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
WHERE id = ?
""",
(data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
)
- row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,))
+ row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,))
assert row, "Newly updated settings couldn't be retrieved"
return Funding(**row) if row else None
async def get_funding() -> List[Funding]:
- rows = await db.fetchall("SELECT * FROM funding")
+ rows = await db.fetchall("SELECT * FROM admin.funding")
return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 0e22e667..c94d140b 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -96,7 +96,7 @@ async def m001_create_admin_table(db):
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS admin (
+ CREATE TABLE IF NOT EXISTS admin.admin (
"user" TEXT PRIMARY KEY,
admin_users TEXT,
allowed_users TEXT,
@@ -120,7 +120,7 @@ async def m001_create_admin_table(db):
)
await db.execute(
"""
- INSERT INTO admin (
+ INSERT INTO admin.admin (
"user",
admin_users,
allowed_users,
@@ -171,7 +171,7 @@ async def m001_create_funding_table(db):
# Make the funding table, if it does not already exist
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS funding (
+ CREATE TABLE IF NOT EXISTS admin.funding (
id TEXT PRIMARY KEY,
backend_wallet TEXT,
endpoint TEXT,
@@ -188,7 +188,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, selected)
VALUES (?, ?, ?, ?)
""",
(
@@ -200,7 +200,7 @@ async def m001_create_funding_table(db):
)
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -214,7 +214,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -228,7 +228,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
@@ -244,7 +244,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
@@ -259,7 +259,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
@@ -274,7 +274,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -288,7 +288,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -302,7 +302,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -317,7 +317,7 @@ async def m001_create_funding_table(db):
## PLACEHOLDER FOR ECLAIR WALLET
# await db.execute(
# """
- # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
# VALUES (?, ?, ?, ?, ?)
# """,
# (
From 08e54de99b72eec2c8f7850ef0d346c15bf64705 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 12:53:47 +0100
Subject: [PATCH 0048/1058] fix sqlite and show user account
---
lnbits/app.py | 1 +
lnbits/commands.py | 2 +-
lnbits/extensions/admin/migrations.py | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 5df439dc..f066163f 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -117,6 +117,7 @@ def check_settings(app: FastAPI):
while True:
admin_set = await get_admin_settings()
if admin_set :
+ print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
break
print("ERROR:", admin_set)
await asyncio.sleep(5)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 7d9b49e2..763a5b90 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -69,7 +69,7 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
- print("EXISTS", exists)
+
if not exists:
return False
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index c94d140b..6c5b507d 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -15,6 +15,7 @@ async def get_admin_user():
user = account.id
assert user, "Newly created user couldn't be retrieved"
print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ conf.admin_users.insert(0, user)
return user
From 1adfb674ccb6bf44ad6e3680c795ed085bd20d8e Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 15:35:04 +0100
Subject: [PATCH 0049/1058] cleanup and info to user on startup
---
lnbits/app.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index f066163f..950b6140 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -113,13 +113,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
-
while True:
admin_set = await get_admin_settings()
if admin_set :
- print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
break
- print("ERROR:", admin_set)
+ print("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
@@ -129,6 +127,7 @@ def check_settings(app: FastAPI):
admin_set.theme = removeEmptyString(admin_set.theme.split(','))
admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
g().admin_conf = conf.copy(update=admin_set.dict())
+ print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
From 363bc85e3b73967c26b9809f1d71664ec8719d8d Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 8 Jun 2022 11:00:43 +0100
Subject: [PATCH 0050/1058] add custom logo
---
lnbits/config.py | 1 +
lnbits/extensions/admin/migrations.py | 58 ++-----------------
lnbits/extensions/admin/models.py | 2 +
.../admin/templates/admin/index.html | 20 +++++--
lnbits/helpers.py | 3 +-
5 files changed, 26 insertions(+), 58 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 3ce51c3c..d07ca044 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -39,6 +39,7 @@ class Settings(BaseSettings):
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
+ custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
env: Optional[str]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 6c5b507d..aad66f02 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -41,60 +41,9 @@ async def m001_create_admin_table(db):
site_description = conf.site_description
default_wallet_name = conf.default_wallet_name
theme = ",".join(conf.theme)
+ custom_logo = conf.custom_logo
ad_space = ",".join(conf.ad_space)
- # if getenv("LNBITS_ADMIN_EXTENSIONS"):
- # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS")
-
- # if getenv("LNBITS_DATABASE_URL"):
- # database_url = getenv("LNBITS_DATABASE_URL")
-
- # if getenv("LNBITS_HIDE_API"):
- # hide_api = getenv("LNBITS_HIDE_API")
-
- # if getenv("LNBITS_THEME_OPTIONS"):
- # theme = getenv("LNBITS_THEME_OPTIONS")
-
- # if getenv("LNBITS_AD_SPACE"):
- # ad_space = getenv("LNBITS_AD_SPACE")
-
- # if getenv("LNBITS_SITE_TITLE"):
- # site_title = getenv("LNBITS_SITE_TITLE")
-
- # if getenv("LNBITS_SITE_TAGLINE"):
- # site_tagline = getenv("LNBITS_SITE_TAGLINE")
-
- # if getenv("LNBITS_SITE_DESCRIPTION"):
- # site_description = getenv("LNBITS_SITE_DESCRIPTION")
-
- # if getenv("LNBITS_ALLOWED_USERS"):
- # allowed_users = getenv("LNBITS_ALLOWED_USERS")
-
- # if getenv("LNBITS_ADMIN_USERS"):
- # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
- # user = admin_users.split(',')[0]
-
- # if getenv("LNBITS_DEFAULT_WALLET_NAME"):
- # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
-
- # if getenv("LNBITS_DATA_FOLDER"):
- # data_folder = getenv("LNBITS_DATA_FOLDER")
-
- # if getenv("LNBITS_DISABLED_EXTENSIONS"):
- # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
-
- # if getenv("LNBITS_FORCE_HTTPS"):
- # force_https = getenv("LNBITS_FORCE_HTTPS")
-
- # if getenv("LNBITS_SERVICE_FEE"):
- # service_fee = getenv("LNBITS_SERVICE_FEE")
-
- # if getenv("LNBITS_DENOMINATION"):
- # denomination = getenv("LNBITS_DENOMINATION", "sats")
-
- # if getenv("LNBITS_BACKEND_WALLET_CLASS"):
- # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
-
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin.admin (
@@ -115,6 +64,7 @@ async def m001_create_admin_table(db):
site_description TEXT,
default_wallet_name TEXT,
theme TEXT,
+ custom_logo TEXT,
ad_space TEXT
);
"""
@@ -139,8 +89,9 @@ async def m001_create_admin_table(db):
site_description,
default_wallet_name,
theme,
+ custom_logo,
ad_space)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
user,
@@ -160,6 +111,7 @@ async def m001_create_admin_table(db):
site_description,
default_wallet_name,
theme,
+ custom_logo,
ad_space,
),
)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 0f25679d..3b17e720 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -23,6 +23,7 @@ class UpdateAdminSettings(BaseModel):
default_wallet_name: str = Query("LNbits wallet")
denomination: str = Query("sats")
theme: str = Query(None)
+ custom_logo: str = Query(None)
ad_space: str = Query(None)
class Admin(BaseModel):
@@ -46,6 +47,7 @@ class Admin(BaseModel):
default_wallet_name: Optional[str]
denomination: str = Field(default="sats")
theme: Optional[str]
+ custom_logo: Optional[str]
ad_space: Optional[str]
@classmethod
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 584d3a33..d9790051 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -705,6 +705,19 @@
+
@@ -718,10 +731,7 @@
-
+
@@ -1292,6 +1323,8 @@
disabled_ext,
funding_source,
force_https,
+ reserve_fee_min,
+ reserve_fee_pct,
service_fee,
hide_api,
site_title,
@@ -1311,6 +1344,8 @@
disabled_ext: disabled_ext.toString(),
funding_source,
force_https,
+ reserve_fee_min,
+ reserve_fee_pct,
service_fee,
hide_api,
site_title,
diff --git a/lnbits/settings.py b/lnbits/settings.py
index ed5c77f7..8e5c321a 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,6 +1,5 @@
import importlib
import subprocess
-from email.policy import default
from os import path
from typing import List
From fcf05a7dd19b831ee986578efad9b2d9094e69bd Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 8 Jun 2022 15:38:28 +0100
Subject: [PATCH 0052/1058] calle's semantics
---
lnbits/extensions/admin/templates/admin/index.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 832629bc..d34b9068 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -67,7 +67,7 @@
-
Minimum wallet reserve
+
Fee reserve
From faab389e3fa21671313b66236e66fccbf1f70a1d Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 5 Jul 2022 16:25:02 +0100
Subject: [PATCH 0053/1058] blacked
---
lnbits/extensions/admin/__init__.py | 1 +
lnbits/extensions/admin/crud.py | 23 ++++++++++++---
lnbits/extensions/admin/migrations.py | 10 ++++---
lnbits/extensions/admin/models.py | 2 ++
lnbits/extensions/admin/views.py | 10 ++++---
lnbits/extensions/admin/views_api.py | 41 ++++++++++++++-------------
6 files changed, 56 insertions(+), 31 deletions(-)
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
index 6a56b2bb..24b91fe2 100644
--- a/lnbits/extensions/admin/__init__.py
+++ b/lnbits/extensions/admin/__init__.py
@@ -7,6 +7,7 @@ db = Database("ext_admin")
admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"])
+
def admin_renderer():
return template_renderer(["lnbits/extensions/admin/templates"])
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 67fbc614..0d7019cc 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -11,13 +11,13 @@ from .models import Admin, Funding
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
temp_id = f"temp_{urlsafe_short_hash()}"
internal_id = f"internal_{urlsafe_short_hash()}"
-
+
payment = await create_payment(
wallet_id=wallet_id,
checking_id=internal_id,
payment_request="admin_internal",
payment_hash="admin_internal",
- amount=amount*1000,
+ amount=amount * 1000,
memo="Admin top up",
pending=False,
)
@@ -25,6 +25,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
await internal_invoice_queue.put(internal_id)
return payment
+
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
@@ -35,23 +36,37 @@ async def update_admin(user: str, **kwargs) -> Admin:
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
+
async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin.admin")
return Admin(**row) if row else None
+
async def update_funding(data: Funding) -> Funding:
await db.execute(
"""
UPDATE admin.funding
SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
WHERE id = ?
- """,
- (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
+ """,
+ (
+ data.backend_wallet,
+ data.endpoint,
+ data.port,
+ data.read_key,
+ data.invoice_key,
+ data.admin_key,
+ data.cert,
+ data.balance,
+ data.selected,
+ data.id,
+ ),
)
row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,))
assert row, "Newly updated settings couldn't be retrieved"
return Funding(**row) if row else None
+
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM admin.funding")
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index f3663435..388f5ec6 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -7,19 +7,21 @@ from lnbits.helpers import urlsafe_short_hash
async def get_admin_user():
- if(conf.admin_users[0]):
+ if conf.admin_users[0]:
return conf.admin_users[0]
from lnbits.core.crud import create_account, get_user
+
print("Seems like there's no admin users yet. Let's create an account for you!")
account = await create_account()
user = account.id
assert user, "Newly created user couldn't be retrieved"
- print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ print(
+ f"Your newly created account/user id is: {user}. This will be the Super Admin user."
+ )
conf.admin_users.insert(0, user)
return user
-
async def m001_create_admin_table(db):
# users/server
user = await get_admin_user()
@@ -28,7 +30,7 @@ async def m001_create_admin_table(db):
admin_ext = ",".join(conf.admin_ext)
disabled_ext = ",".join(conf.disabled_ext)
funding_source = conf.funding_source
- #operational
+ # operational
data_folder = conf.data_folder
database_url = conf.database_url
force_https = conf.force_https
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 3d8efdcd..6e95d68f 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -28,6 +28,7 @@ class UpdateAdminSettings(BaseModel):
custom_logo: str = Query(None)
ad_space: str = Query(None)
+
class Admin(BaseModel):
# users
user: str
@@ -59,6 +60,7 @@ class Admin(BaseModel):
data = dict(row)
return cls(**data)
+
class Funding(BaseModel):
id: str
backend_wallet: str
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 24b8ca85..ceda5192 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -16,19 +16,21 @@ from .crud import get_admin, get_funding
templates = Jinja2Templates(directory="templates")
+
@admin_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
funding = [f.dict() for f in await get_funding()]
error, balance = await g().WALLET.status()
-
+
return admin_renderer().TemplateResponse(
- "admin/index.html", {
+ "admin/index.html",
+ {
"request": request,
"user": user.dict(),
"admin": admin.dict(),
"funding": funding,
"settings": g().admin_conf.dict(),
- "balance": balance
- }
+ "balance": balance,
+ },
)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c0650c8a..784ad97f 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -15,16 +15,18 @@ from .crud import get_admin, update_admin, update_funding, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
-async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)):
+async def api_update_balance(
+ wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)
+):
try:
wallet = await get_wallet(wallet_id)
except:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
-
+
return {"status": "Success"}
@@ -32,39 +34,40 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
async def api_update_admin(
request: Request,
data: UpdateAdminSettings = Body(...),
- w: WalletTypeInfo = Depends(require_admin_key)
- ):
+ w: WalletTypeInfo = Depends(require_admin_key),
+):
admin = await get_admin()
# print(data)
if not admin.user == w.wallet.user:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
updated = await update_admin(user=w.wallet.user, **data.dict())
- updated.admin_users = removeEmptyString(updated.admin_users.split(','))
- updated.allowed_users = removeEmptyString(updated.allowed_users.split(','))
- updated.admin_ext = removeEmptyString(updated.admin_ext.split(','))
- updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(','))
- updated.theme = removeEmptyString(updated.theme.split(','))
- updated.ad_space = removeEmptyString(updated.ad_space.split(','))
+ updated.admin_users = removeEmptyString(updated.admin_users.split(","))
+ updated.allowed_users = removeEmptyString(updated.allowed_users.split(","))
+ updated.admin_ext = removeEmptyString(updated.admin_ext.split(","))
+ updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(","))
+ updated.theme = removeEmptyString(updated.theme.split(","))
+ updated.ad_space = removeEmptyString(updated.ad_space.split(","))
g().admin_conf = g().admin_conf.copy(update=updated.dict())
-
+
# print(g().admin_conf)
return {"status": "Success"}
+
@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
async def api_update_funding(
request: Request,
data: Funding = Body(...),
- w: WalletTypeInfo = Depends(require_admin_key)
- ):
+ w: WalletTypeInfo = Depends(require_admin_key),
+):
admin = await get_admin()
if not admin.user == w.wallet.user:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
funding = await update_funding(data=data)
return funding
From c319b84d7299eb5f7d942214fab2f21deffb38f6 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:28:13 +0100
Subject: [PATCH 0054/1058] Had to add a couple of tries
---
lnbits/core/views/generic.py | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 83648c44..63f7af68 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -133,12 +133,19 @@ async def wallet(
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
)
- if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
- return template_renderer().TemplateResponse(
- "error.html", {"request": request, "err": "User not authorized."}
- )
- if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
- user.admin = True
+ try:
+ if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
+ return template_renderer().TemplateResponse(
+ "error.html", {"request": request, "err": "User not authorized."}
+ )
+ except:
+ pass
+
+ try:
+ if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
+ user.admin = True
+ except:
+ pass
if not wallet_id:
if user.wallets and not wallet_name: # type: ignore
wallet = user.wallets[0] # type: ignore
From d8a13ed29d323f5233c6d9ba5cb59f9ef352d90c Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:31:31 +0100
Subject: [PATCH 0055/1058] Added couple more tries
---
lnbits/decorators.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index f951163f..a810892d 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -239,13 +239,16 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
-
- if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
- raise HTTPException(
- status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
- )
-
- if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
- g().user.admin = True
-
+ try:
+ if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ except:
+ pass
+ try:
+ if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
+ g().user.admin = True
+ except:
+ pass
return g().user
From 55a44030283b34b6d61c0d441b779b135b449c3a Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:35:06 +0100
Subject: [PATCH 0056/1058] Reverted try
---
lnbits/decorators.py | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index a810892d..904ca1c2 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -239,16 +239,12 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
- try:
- if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
- raise HTTPException(
- status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
- )
- except:
- pass
- try:
- if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
- g().user.admin = True
+ if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
+ g().user.admin = True
except:
pass
return g().user
From a932f8a3d0c43692f6ec2b1ae9284c279ab7396c Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:37:07 +0100
Subject: [PATCH 0057/1058] reverted other try
---
lnbits/core/views/generic.py | 19 ++++++-------------
lnbits/decorators.py | 2 --
2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 63f7af68..83648c44 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -133,19 +133,12 @@ async def wallet(
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
)
- try:
- if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
- return template_renderer().TemplateResponse(
- "error.html", {"request": request, "err": "User not authorized."}
- )
- except:
- pass
-
- try:
- if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
- user.admin = True
- except:
- pass
+ if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
+ return template_renderer().TemplateResponse(
+ "error.html", {"request": request, "err": "User not authorized."}
+ )
+ if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
+ user.admin = True
if not wallet_id:
if user.wallets and not wallet_name: # type: ignore
wallet = user.wallets[0] # type: ignore
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 904ca1c2..dd26d8fe 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -245,6 +245,4 @@ async def check_user_exists(usr: UUID4) -> User:
)
if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
g().user.admin = True
- except:
- pass
return g().user
From c32e0cbecb147918b2c95ad29bcaf8876855ac0b Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 21 Sep 2022 18:40:46 +0100
Subject: [PATCH 0058/1058] fix main merge missing settings
---
lnbits/app.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lnbits/app.py b/lnbits/app.py
index 950b6140..49ef3d1b 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -56,6 +56,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
},
)
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ g().admin_conf = conf
+ check_settings(app)
+
+ g().WALLET = WALLET
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
"/core/static",
From e4c310d197fbb6ddb9a7d5528aab66fe60608313 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 10:46:11 +0200
Subject: [PATCH 0059/1058] format
---
lnbits/app.py | 22 +++++++++++++---------
lnbits/commands.py | 6 +++++-
lnbits/config.py | 25 ++++++++++++++++++-------
lnbits/core/views/generic.py | 1 +
lnbits/decorators.py | 2 +-
lnbits/helpers.py | 8 +++++---
6 files changed, 43 insertions(+), 21 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 49ef3d1b..00ed6d8d 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -115,24 +115,28 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
+
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
while True:
admin_set = await get_admin_settings()
- if admin_set :
+ if admin_set:
break
print("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
-
- admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
- admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(','))
- admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(','))
- admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(','))
- admin_set.theme = removeEmptyString(admin_set.theme.split(','))
- admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
+
+ admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(","))
+ admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(","))
+ admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(","))
+ admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(","))
+ admin_set.theme = removeEmptyString(admin_set.theme.split(","))
+ admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(","))
g().admin_conf = conf.copy(update=admin_set.dict())
- print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
+ print(
+ f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}"
+ )
+
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 763a5b90..86868f1f 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -5,6 +5,7 @@ import re
import warnings
import click
+from genericpath import exists
from loguru import logger
from .core import db as core_db
@@ -52,6 +53,7 @@ def bundle_vendored():
with open(outputpath, "w") as f:
f.write(output)
+
async def get_admin_settings():
from lnbits.extensions.admin.models import Admin
@@ -61,6 +63,7 @@ async def get_admin_settings():
return False
async with ext_db.connect() as conn:
+
if conn.type == SQLITE:
exists = await conn.fetchone(
"SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
@@ -69,7 +72,7 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
-
+
if not exists:
return False
@@ -77,6 +80,7 @@ async def get_admin_settings():
return Admin(**row) if row else None
+
async def migrate_databases():
"""Creates the necessary databases if they don't exist already; or migrates them."""
diff --git a/lnbits/config.py b/lnbits/config.py
index 37b700fd..cf26ad21 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -6,17 +6,19 @@ from typing import List, Optional
from pydantic import BaseSettings, Field
wallets_module = importlib.import_module("lnbits.wallets")
-wallet_class = getattr(
+wallet_class = getattr(
wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet")
)
WALLET = wallet_class()
+
def list_parse_fallback(v):
try:
return json.loads(v)
except Exception as e:
- return v.replace(' ','').split(',')
+ return v.replace(" ", "").split(",")
+
class Settings(BaseSettings):
admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI")
@@ -24,7 +26,9 @@ class Settings(BaseSettings):
admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS")
- disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS")
+ disabled_ext: List[str] = Field(
+ default_factory=list, env="LNBITS_DISABLED_EXTENSIONS"
+ )
funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS")
# ops
data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER")
@@ -37,10 +41,17 @@ class Settings(BaseSettings):
denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
# Change theme
site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE")
- site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
+ site_tagline: str = Field(
+ default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE"
+ )
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
- default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
- theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
+ default_wallet_name: str = Field(
+ default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME"
+ )
+ theme: List[str] = Field(
+ default=["classic, flamingo, mint, salvador, monochrome, autumn"],
+ env="LNBITS_THEME_OPTIONS",
+ )
custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
@@ -48,7 +59,7 @@ class Settings(BaseSettings):
debug: Optional[str]
host: Optional[str]
port: Optional[str]
- lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
+ lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
# @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True)
# def validate(cls, val):
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 83648c44..3a1fbdfc 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -39,6 +39,7 @@ from ..services import pay_invoice, redeem_lnurl_withdraw
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
+
@core_html_routes.get("/favicon.ico", response_class=FileResponse)
async def favicon():
return FileResponse("lnbits/core/static/favicon.ico")
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index dd26d8fe..58b025aa 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -235,7 +235,7 @@ async def check_user_exists(usr: UUID4) -> User:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
-
+
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index 7bd1b54a..f4255c86 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -157,11 +157,13 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s
url = f"{base}{endpoint}{url_params}"
return url
+
def removeEmptyString(arr):
return list(filter(None, arr))
+
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
- if(settings.LNBITS_ADMIN_UI):
+ if settings.LNBITS_ADMIN_UI:
_ = g().admin_conf
settings.LNBITS_AD_SPACE = _.ad_space
settings.LNBITS_HIDE_API = _.hide_api
@@ -170,8 +172,8 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
settings.LNBITS_SITE_TAGLINE = _.site_tagline
settings.LNBITS_SITE_DESCRIPTION = _.site_description
settings.LNBITS_THEME_OPTIONS = _.theme
- settings.LNBITS_CUSTOM_LOGO = _.custom_logo
-
+ settings.LNBITS_CUSTOM_LOGO = _.custom_logo
+
t = Jinja2Templates(
loader=jinja2.FileSystemLoader(
["lnbits/templates", "lnbits/core/templates", *additional_folders]
From 6c2a9b2258087302487716e3d35b1b2387bb58ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 11:47:24 +0200
Subject: [PATCH 0060/1058] format black
---
lnbits/app.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/app.py b/lnbits/app.py
index 00ed6d8d..6ae75d7a 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -56,6 +56,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
},
)
+
if lnbits.settings.LNBITS_ADMIN_UI:
g().admin_conf = conf
check_settings(app)
From 7c05d4c354be10d502b7c453f96902f76f9c15d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 15:29:12 +0200
Subject: [PATCH 0061/1058] fix AD_SPACE
---
lnbits/core/templates/core/wallet.html | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index bccdc2b4..189b060b 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -385,12 +385,9 @@
- {% endif %} {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD =
- ADS.split(';') %}
+ {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %}
- {% endfor %} {% endif %}
From 415165c6fe7ec2affda20078c81e12b4fc2944de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 15:55:37 +0200
Subject: [PATCH 0062/1058] fix some javascript errors when adding users
---
lnbits/extensions/admin/templates/admin/index.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d34b9068..1e881cb6 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1221,7 +1221,7 @@
addAdminUser() {
let addUser = this.data.admin_users_add
let admin_users = this.data.admin.admin_users
- if (addUser.length && !admin_users.includes(addUser)) {
+ if (addUser && addUser.length && !admin_users.includes(addUser)) {
admin_users.push(addUser)
this.data.admin.admin_users = admin_users
this.data.admin_users_add = ''
@@ -1234,7 +1234,7 @@
addAllowedUser() {
let addUser = this.data.allowed_users_add
let allowed_users = this.data.admin.allowed_users
- if (addUser.length && !allowed_users.includes(addUser)) {
+ if (addUser && addUser.length && !allowed_users.includes(addUser)) {
allowed_users.push(addUser)
this.data.admin.allowed_users = allowed_users
this.data.allowed_users_add = ''
@@ -1336,7 +1336,6 @@
custom_logo,
ad_space
} = this.data.admin
- //console.log("this", this.data.admin)
let data = {
admin_users: admin_users.toString(),
allowed_users: allowed_users.toString(),
From 850ed85311fea5c5e10bca8cdeaaa0a3dffbe71f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 16:04:55 +0200
Subject: [PATCH 0063/1058] fix ADMIN_UI=false errors
---
lnbits/core/views/generic.py | 3 +++
lnbits/decorators.py | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 3a1fbdfc..db4fac43 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -124,6 +124,9 @@ async def wallet(
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+ else:
+ LNBITS_ADMIN_USERS = []
+ LNBITS_ALLOWED_USERS = []
if not user_id:
user = await get_user((await create_account()).id)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 58b025aa..5a3c0a5c 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -141,6 +141,8 @@ async def get_key_type(
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ else:
+ LNBITS_ADMIN_USERS = []
for typenr, WalletChecker in zip(
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
@@ -239,6 +241,10 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+ else:
+ LNBITS_ADMIN_USERS = []
+ LNBITS_ALLOWED_USERS = []
+
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
From 393d1a8204f04498aca712604f5646dcb30d7746 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 16:14:17 +0200
Subject: [PATCH 0064/1058] prettier
---
lnbits/core/templates/core/wallet.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index 189b060b..22c65bf3 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -386,8 +386,7 @@
{% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %}
-
- {% endfor %} {% endif %}
From 622d0f50da4b319765175c97613ab31e5e1dd722 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 18:35:47 +0200
Subject: [PATCH 0065/1058] fix migration tests
---
lnbits/extensions/admin/migrations.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 388f5ec6..196c9fc0 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -7,7 +7,7 @@ from lnbits.helpers import urlsafe_short_hash
async def get_admin_user():
- if conf.admin_users[0]:
+ if len(conf.admin_users) > 0:
return conf.admin_users[0]
from lnbits.core.crud import create_account, get_user
From 26769d94984c5d7c4f56b1bd06ca4ce3e376453c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 26 Sep 2022 16:54:19 +0200
Subject: [PATCH 0066/1058] change comments to use multiple lines
---
.env.example | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/.env.example b/.env.example
index 7a49d5c5..c1ac7497 100644
--- a/.env.example
+++ b/.env.example
@@ -3,11 +3,15 @@ PORT=5000
DEBUG=false
-LNBITS_ADMIN_USERS="" # User IDs seperated by comma
-LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
+# User IDs seperated by comma
+LNBITS_ADMIN_USERS=""
+# Extensions only admin can access
+LNBITS_ADMIN_EXTENSIONS="ngrok, admin"
+# Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
+LNBITS_ADMIN_UI=false
-LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
+# Restricts access, User IDs seperated by comma
+LNBITS_ALLOWED_USERS=""
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
From cadb6b21617c77fac36ea55937a03c223ec5aa56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 26 Sep 2022 16:59:30 +0200
Subject: [PATCH 0067/1058] fix merge error
---
lnbits/app.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 6ae75d7a..60c09038 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -57,10 +57,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
- if lnbits.settings.LNBITS_ADMIN_UI:
- g().admin_conf = conf
- check_settings(app)
-
g().WALLET = WALLET
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
From a78ebbacd700c7975662424e07bb042a2da380f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:14:36 +0200
Subject: [PATCH 0068/1058] use logger in app.py
---
lnbits/app.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 60c09038..d7bd3ea6 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -31,8 +31,6 @@ from .helpers import (
url_for_vendored,
)
from .requestvars import g
-
-# from .settings import WALLET
from .tasks import (
catch_everything_and_restart,
check_pending_payments,
@@ -120,7 +118,7 @@ def check_settings(app: FastAPI):
admin_set = await get_admin_settings()
if admin_set:
break
- print("Waiting for admin settings... retrying in 5 seconds!")
+ logger.info("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(","))
@@ -130,7 +128,7 @@ def check_settings(app: FastAPI):
admin_set.theme = removeEmptyString(admin_set.theme.split(","))
admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(","))
g().admin_conf = conf.copy(update=admin_set.dict())
- print(
+ logger.info(
f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}"
)
From 8be9b950fdff51e8c9d94d4123cae0dfb650dd3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:14:57 +0200
Subject: [PATCH 0069/1058] fix config
---
lnbits/config.py | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index cf26ad21..874effae 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -17,7 +17,11 @@ def list_parse_fallback(v):
try:
return json.loads(v)
except Exception as e:
- return v.replace(" ", "").split(",")
+ replaced = v.replace(" ", "")
+ if replaced:
+ return replaced.split(",")
+ else:
+ return []
class Settings(BaseSettings):
@@ -48,10 +52,7 @@ class Settings(BaseSettings):
default_wallet_name: str = Field(
default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME"
)
- theme: List[str] = Field(
- default=["classic, flamingo, mint, salvador, monochrome, autumn"],
- env="LNBITS_THEME_OPTIONS",
- )
+ theme: List[str] = Field(default_factory=list, env="LNBITS_THEME_OPTIONS")
custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
@@ -74,4 +75,5 @@ class Settings(BaseSettings):
conf = Settings()
+print(conf)
WALLET = wallet_class()
From f2e494384e96587b0b0fb2a9de8825aa9115ee21 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:17:20 +0200
Subject: [PATCH 0070/1058] concatenate first migrations script
fixup
---
lnbits/config.py | 1 -
lnbits/extensions/admin/migrations.py | 7 +++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 874effae..fe8dabf9 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -75,5 +75,4 @@ class Settings(BaseSettings):
conf = Settings()
-print(conf)
WALLET = wallet_class()
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 196c9fc0..2d48a8e4 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -23,6 +23,8 @@ async def get_admin_user():
async def m001_create_admin_table(db):
+
+
# users/server
user = await get_admin_user()
admin_users = ",".join(conf.admin_users)
@@ -78,7 +80,7 @@ async def m001_create_admin_table(db):
await db.execute(
"""
INSERT INTO admin.admin (
- "user",
+ "user",
admin_users,
allowed_users,
admin_ext,
@@ -126,9 +128,6 @@ async def m001_create_admin_table(db):
),
)
-
-async def m001_create_funding_table(db):
-
funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
# Make the funding table, if it does not already exist
From 9757fad8684d166ce9ac04e6a69b0ff820c4de61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 16:37:08 +0200
Subject: [PATCH 0071/1058] add restart button to frontend
---
.../admin/templates/admin/index.html | 28 ++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 1e881cb6..319ca3f0 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -65,6 +65,14 @@
:options="data.funding_source"
>
+
+
+
Fee reserve
@@ -89,7 +97,7 @@
>
-
+
@@ -1257,6 +1265,24 @@
let spaces = this.data.admin.ad_space
this.data.admin.ad_space = spaces.filter(s => s !== ad)
},
+ restartServer() {
+ LNbits.api
+ .request(
+ 'GET',
+ '/admin/api/v1/admin/restart/',
+ this.g.user.wallets[0].adminkey
+ )
+ .then(response => {
+ this.$q.notify({
+ type: 'positive',
+ message: 'Success! Restarted Server',
+ icon: null
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
topupWallet() {
LNbits.api
.request(
From 66739f4246bdc6d3b347f54f675c00937601935e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:34:52 +0200
Subject: [PATCH 0072/1058] make extension use new settings
---
lnbits/extensions/boltz/boltz.py | 14 ++++++--------
lnbits/extensions/boltz/mempool.py | 15 ++++++---------
lnbits/extensions/boltz/views_api.py | 4 ++--
lnbits/extensions/lndhub/views_api.py | 4 ++--
lnbits/extensions/tpos/views.py | 14 +++++++-------
5 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/lnbits/extensions/boltz/boltz.py b/lnbits/extensions/boltz/boltz.py
index ac99d4f4..424d0bf7 100644
--- a/lnbits/extensions/boltz/boltz.py
+++ b/lnbits/extensions/boltz/boltz.py
@@ -12,7 +12,7 @@ from loguru import logger
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.helpers import urlsafe_short_hash
-from lnbits.settings import BOLTZ_NETWORK, BOLTZ_URL
+from lnbits.settings import settings
from .crud import update_swap_status
from .mempool import (
@@ -33,9 +33,7 @@ from .models import (
)
from .utils import check_balance, get_timestamp, req_wrap
-net = NETWORKS[BOLTZ_NETWORK]
-logger.trace(f"BOLTZ_URL: {BOLTZ_URL}")
-logger.trace(f"Bitcoin Network: {net['name']}")
+net = NETWORKS[settings.boltz_network]
async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
@@ -62,7 +60,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
res = req_wrap(
"post",
- f"{BOLTZ_URL}/createswap",
+ f"{settings.boltz_url}/createswap",
json={
"type": "submarine",
"pairId": "BTC/BTC",
@@ -129,7 +127,7 @@ async def create_reverse_swap(
res = req_wrap(
"post",
- f"{BOLTZ_URL}/createswap",
+ f"{settings.boltz_url}/createswap",
json={
"type": "reversesubmarine",
"pairId": "BTC/BTC",
@@ -409,7 +407,7 @@ def check_boltz_limits(amount):
def get_boltz_pairs():
res = req_wrap(
"get",
- f"{BOLTZ_URL}/getpairs",
+ f"{settings.boltz_url}/getpairs",
headers={"Content-Type": "application/json"},
)
return res.json()
@@ -418,7 +416,7 @@ def get_boltz_pairs():
def get_boltz_status(boltzid):
res = req_wrap(
"post",
- f"{BOLTZ_URL}/swapstatus",
+ f"{settings.boltz_url}/swapstatus",
json={"id": boltzid},
)
return res.json()
diff --git a/lnbits/extensions/boltz/mempool.py b/lnbits/extensions/boltz/mempool.py
index a44c0f02..a64cadad 100644
--- a/lnbits/extensions/boltz/mempool.py
+++ b/lnbits/extensions/boltz/mempool.py
@@ -7,14 +7,11 @@ import websockets
from embit.transaction import Transaction
from loguru import logger
-from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL, BOLTZ_MEMPOOL_SPACE_URL_WS
+from lnbits.settings import settings
from .utils import req_wrap
-logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}")
-logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}")
-
-websocket_url = f"{BOLTZ_MEMPOOL_SPACE_URL_WS}/api/v1/ws"
+websocket_url = f"{settings.boltz_mempool_space_url_ws}/api/v1/ws"
async def wait_for_websocket_message(send, message_string):
@@ -33,7 +30,7 @@ async def wait_for_websocket_message(send, message_string):
def get_mempool_tx(address):
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/address/{address}/txs",
+ f"{settings.boltz_mempool_space_url}/api/address/{address}/txs",
headers={"Content-Type": "text/plain"},
)
txs = res.json()
@@ -70,7 +67,7 @@ def get_fee_estimation() -> int:
def get_mempool_fees() -> int:
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/v1/fees/recommended",
+ f"{settings.boltz_mempool_space_url}/api/v1/fees/recommended",
headers={"Content-Type": "text/plain"},
)
fees = res.json()
@@ -80,7 +77,7 @@ def get_mempool_fees() -> int:
def get_mempool_blockheight() -> int:
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/blocks/tip/height",
+ f"{settings.boltz_mempool_space_url}/api/blocks/tip/height",
headers={"Content-Type": "text/plain"},
)
return int(res.text)
@@ -91,7 +88,7 @@ async def send_onchain_tx(tx: Transaction):
logger.debug(f"Boltz - mempool sending onchain tx...")
req_wrap(
"post",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/tx",
+ f"{settings.boltz_mempool_space_url}/api/tx",
headers={"Content-Type": "text/plain"},
content=raw,
)
diff --git a/lnbits/extensions/boltz/views_api.py b/lnbits/extensions/boltz/views_api.py
index a4b7d318..18ca14cb 100644
--- a/lnbits/extensions/boltz/views_api.py
+++ b/lnbits/extensions/boltz/views_api.py
@@ -14,7 +14,7 @@ from starlette.requests import Request
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL
+from lnbits.settings import settings
from . import boltz_ext
from .boltz import (
@@ -55,7 +55,7 @@ from .utils import check_balance
response_model=str,
)
async def api_mempool_url():
- return BOLTZ_MEMPOOL_SPACE_URL
+ return settings.boltz_mempool_space_url
# NORMAL SWAP
diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py
index 8cbe5a6b..b2328c39 100644
--- a/lnbits/extensions/lndhub/views_api.py
+++ b/lnbits/extensions/lndhub/views_api.py
@@ -12,7 +12,7 @@ from lnbits import bolt11
from lnbits.core.crud import delete_expired_invoices, get_payments
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.decorators import WalletTypeInfo
-from lnbits.settings import LNBITS_SITE_TITLE, WALLET
+from lnbits.settings import WALLET, settings
from . import lndhub_ext
from .decorators import check_wallet, require_admin_key
@@ -56,7 +56,7 @@ async def lndhub_addinvoice(
_, pr = await create_invoice(
wallet_id=wallet.wallet.id,
amount=int(data.amt),
- memo=data.memo or LNBITS_SITE_TITLE,
+ memo=data.memo or settings.lnbits_site_title,
extra={"tag": "lndhub"},
)
except:
diff --git a/lnbits/extensions/tpos/views.py b/lnbits/extensions/tpos/views.py
index e1f1d21e..dac129a9 100644
--- a/lnbits/extensions/tpos/views.py
+++ b/lnbits/extensions/tpos/views.py
@@ -8,7 +8,7 @@ from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
-from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE
+from lnbits.settings import settings
from . import tpos_ext, tpos_renderer
from .crud import get_tpos
@@ -50,12 +50,12 @@ async def manifest(tpos_id: str):
)
return {
- "short_name": LNBITS_SITE_TITLE,
- "name": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "short_name": settings.lnbits_site_title,
+ "name": tpos.name + " - " + settings.lnbits_site_title,
"icons": [
{
- "src": LNBITS_CUSTOM_LOGO
- if LNBITS_CUSTOM_LOGO
+ "src": settings.lnbits_custom_logo
+ if settings.lnbits_custom_logo
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
"type": "image/png",
"sizes": "900x900",
@@ -69,9 +69,9 @@ async def manifest(tpos_id: str):
"theme_color": "#1F2234",
"shortcuts": [
{
- "name": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "name": tpos.name + " - " + settings.lnbits_site_title,
"short_name": tpos.name,
- "description": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "description": tpos.name + " - " + settings.lnbits_site_title,
"url": "/tpos/" + tpos_id,
}
],
From 2b2884142fd9c2ac6fbfa3cbd8916aaf2225068a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:35:26 +0200
Subject: [PATCH 0073/1058] remove enviroms
---
pyproject.toml | 1 -
1 file changed, 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 19dac860..7a69ec43 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,7 +20,6 @@ charset-normalizer = "2.0.6"
click = "8.0.1"
ecdsa = "0.17.0"
embit = "0.4.9"
-environs = "9.3.3"
fastapi = "0.78.0"
h11 = "0.12.0"
httpcore = "0.15.0"
From 840ede1bd66fd437e342bbefc0880de6fc2f216c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:36:14 +0200
Subject: [PATCH 0074/1058] fix admin
---
lnbits/extensions/admin/crud.py | 24 +-
lnbits/extensions/admin/migrations.py | 337 +----
lnbits/extensions/admin/models.py | 58 +-
.../admin/templates/admin/_tab_funding.html | 158 +++
.../admin/templates/admin/_tab_server.html | 78 ++
.../admin/templates/admin/_tab_theme.html | 122 ++
.../admin/templates/admin/_tab_users.html | 96 ++
.../admin/templates/admin/index.html | 1133 +----------------
lnbits/extensions/admin/views.py | 16 +-
lnbits/extensions/admin/views_api.py | 28 +-
10 files changed, 576 insertions(+), 1474 deletions(-)
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_funding.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_server.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_theme.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_users.html
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 0d7019cc..e4cb5d77 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -2,10 +2,11 @@ from typing import List
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
+from lnbits.settings import Settings
from lnbits.tasks import internal_invoice_queue
from . import db
-from .models import Admin, Funding
+from .models import Funding
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -23,26 +24,26 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
)
# manually send this for now
await internal_invoice_queue.put(internal_id)
- return payment
-async def update_admin(user: str, **kwargs) -> Admin:
+async def update_settings(user: str, **kwargs) -> Settings:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
await db.execute(
- f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
+ f'UPDATE admin.settings SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,))
+ row = await db.fetchone('SELECT * FROM admin.settings WHERE "user" = ?', (user,))
assert row, "Newly updated settings couldn't be retrieved"
- return Admin(**row) if row else None
-
-
-async def get_admin() -> Admin:
- row = await db.fetchone("SELECT * FROM admin.admin")
- return Admin(**row) if row else None
+ return Settings(**row) if row else None
async def update_funding(data: Funding) -> Funding:
+ await db.execute(
+ """
+ UPDATE admin.settings SET funding_source = ? WHERE user = ?
+ """,
+ (data.backend_wallet, data.user),
+ )
await db.execute(
"""
UPDATE admin.funding
@@ -69,5 +70,4 @@ async def update_funding(data: Funding) -> Funding:
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM admin.funding")
-
return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 2d48a8e4..8f6c76a0 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -1,292 +1,57 @@
-from os import getenv
-
-from sqlalchemy.exc import OperationalError # type: ignore
-
-from lnbits.config import conf
-from lnbits.helpers import urlsafe_short_hash
-
-
-async def get_admin_user():
- if len(conf.admin_users) > 0:
- return conf.admin_users[0]
- from lnbits.core.crud import create_account, get_user
-
- print("Seems like there's no admin users yet. Let's create an account for you!")
- account = await create_account()
- user = account.id
- assert user, "Newly created user couldn't be retrieved"
- print(
- f"Your newly created account/user id is: {user}. This will be the Super Admin user."
- )
- conf.admin_users.insert(0, user)
- return user
-
-
-async def m001_create_admin_table(db):
-
-
- # users/server
- user = await get_admin_user()
- admin_users = ",".join(conf.admin_users)
- allowed_users = ",".join(conf.allowed_users)
- admin_ext = ",".join(conf.admin_ext)
- disabled_ext = ",".join(conf.disabled_ext)
- funding_source = conf.funding_source
- # operational
- data_folder = conf.data_folder
- database_url = conf.database_url
- force_https = conf.force_https
- reserve_fee_min = conf.reserve_fee_min
- reserve_fee_pct = conf.reserve_fee_pct
- service_fee = conf.service_fee
- hide_api = conf.hide_api
- denomination = conf.denomination
- # Theme'ing
- site_title = conf.site_title
- site_tagline = conf.site_tagline
- site_description = conf.site_description
- default_wallet_name = conf.default_wallet_name
- theme = ",".join(conf.theme)
- custom_logo = conf.custom_logo
- ad_space = ",".join(conf.ad_space)
-
+async def m001_create_admin_settings_table(db):
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS admin.admin (
- "user" TEXT PRIMARY KEY,
- admin_users TEXT,
- allowed_users TEXT,
- admin_ext TEXT,
- disabled_ext TEXT,
- funding_source TEXT,
- data_folder TEXT,
- database_url TEXT,
- force_https BOOLEAN,
- reserve_fee_min INT,
- reserve_fee_pct REAL,
- service_fee REAL,
- hide_api BOOLEAN,
- denomination TEXT,
- site_title TEXT,
- site_tagline TEXT,
- site_description TEXT,
- default_wallet_name TEXT,
- theme TEXT,
- custom_logo TEXT,
- ad_space TEXT
- );
- """
- )
- await db.execute(
- """
- INSERT INTO admin.admin (
- "user",
- admin_users,
- allowed_users,
- admin_ext,
- disabled_ext,
- funding_source,
- data_folder,
- database_url,
- force_https,
- reserve_fee_min,
- reserve_fee_pct,
- service_fee,
- hide_api,
- denomination,
- site_title,
- site_tagline,
- site_description,
- default_wallet_name,
- theme,
- custom_logo,
- ad_space)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- user,
- admin_users,
- allowed_users,
- admin_ext,
- disabled_ext,
- funding_source,
- data_folder,
- database_url,
- force_https,
- reserve_fee_min,
- reserve_fee_pct,
- service_fee,
- hide_api,
- denomination,
- site_title,
- site_tagline,
- site_description,
- default_wallet_name,
- theme,
- custom_logo,
- ad_space,
- ),
- )
-
- funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
-
- # Make the funding table, if it does not already exist
- await db.execute(
- """
- CREATE TABLE IF NOT EXISTS admin.funding (
- id TEXT PRIMARY KEY,
- backend_wallet TEXT,
- endpoint TEXT,
+ CREATE TABLE IF NOT EXISTS admin.settings (
+ lnbits_admin_ui TEXT,
+ debug TEXT,
+ host TEXT,
port INT,
- read_key TEXT,
- invoice_key TEXT,
- admin_key TEXT,
- cert TEXT,
- balance INT,
- selected INT
+ lnbits_path TEXT,
+ lnbits_commit TEXT,
+ lnbits_admin_users TEXT,
+ lnbits_allowed_users TEXT,
+ lnbits_allowed_funding_sources TEXT,
+ lnbits_admin_extensions TEXT,
+ lnbits_disabled_extensions TEXT,
+ lnbits_site_title TEXT,
+ lnbits_site_tagline TEXT,
+ lnbits_site_description TEXT,
+ lnbits_default_wallet_name TEXT,
+ lnbits_theme_options TEXT,
+ lnbits_custom_logo TEXT,
+ lnbits_ad_space TEXT,
+ lnbits_data_folder TEXT,
+ lnbits_database_url TEXT,
+ lnbits_force_https TEXT,
+ lnbits_reserve_fee_min TEXT,
+ lnbits_reserve_fee_percent TEXT,
+ lnbits_service_fee TEXT,
+ lnbits_hide_api TEXT,
+ lnbits_denomination TEXT,
+ lnbits_backend_wallet_class TEXT,
+ fake_wallet_secret TEXT,
+ lnbits_endpoint TEXT,
+ lnbits_key TEXT,
+ cliche_endpoint TEXT,
+ corelightning_rpc TEXT,
+ eclair_url TEXT,
+ eclair_pass TEXT,
+ lnd_rest_endpoint TEXT,
+ lnd_rest_cert TEXT,
+ lnd_rest_macaroon TEXT,
+ lnpay_api_endpoint TEXT,
+ lnpay_api_key TEXT,
+ lnpay_wallet_key TEXT,
+ lntxbot_api_endpoint TEXT,
+ lntxbot_key TEXT,
+ opennode_api_endpoint TEXT,
+ opennode_key TEXT,
+ spark_url TEXT,
+ spark_token TEXT,
+ boltz_network TEXT,
+ boltz_url TEXT,
+ boltz_mempool_space_url TEXT,
+ boltz_mempool_space_url_ws TEXT
);
"""
)
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, selected)
- VALUES (?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "CLightningWallet",
- getenv("CLIGHTNING_RPC"),
- 1 if funding_wallet == "CLightningWallet" else 0,
- ),
- )
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "SparkWallet",
- getenv("SPARK_URL"),
- getenv("SPARK_TOKEN"),
- 1 if funding_wallet == "SparkWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LnbitsWallet",
- getenv("LNBITS_ENDPOINT"),
- getenv("LNBITS_KEY"),
- 1 if funding_wallet == "LnbitsWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LndWallet",
- getenv("LND_GRPC_ENDPOINT"),
- getenv("LND_GRPC_PORT"),
- getenv("LND_GRPC_MACAROON"),
- getenv("LND_GRPC_CERT"),
- 1 if funding_wallet == "LndWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LndRestWallet",
- getenv("LND_REST_ENDPOINT"),
- getenv("LND_REST_MACAROON"),
- getenv("LND_REST_CERT"),
- 1 if funding_wallet == "LndWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LNPayWallet",
- getenv("LNPAY_API_ENDPOINT"),
- getenv("LNPAY_WALLET_KEY"),
- getenv("LNPAY_API_KEY"), # this is going in as the cert
- 1 if funding_wallet == "LNPayWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LntxbotWallet",
- getenv("LNTXBOT_API_ENDPOINT"),
- getenv("LNTXBOT_KEY"),
- 1 if funding_wallet == "LntxbotWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "OpenNodeWallet",
- getenv("OPENNODE_API_ENDPOINT"),
- getenv("OPENNODE_KEY"),
- 1 if funding_wallet == "OpenNodeWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "SparkWallet",
- getenv("SPARK_URL"),
- getenv("SPARK_TOKEN"),
- 1 if funding_wallet == "SparkWallet" else 0,
- ),
- )
-
- ## PLACEHOLDER FOR ECLAIR WALLET
- # await db.execute(
- # """
- # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- # VALUES (?, ?, ?, ?, ?)
- # """,
- # (
- # urlsafe_short_hash(),
- # "EclairWallet",
- # getenv("ECLAIR_URL"),
- # getenv("ECLAIR_PASS"),
- # 1 if funding_wallet == "EclairWallet" else 0,
- # ),
- # )
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 6e95d68f..ef57cadd 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -29,36 +29,36 @@ class UpdateAdminSettings(BaseModel):
ad_space: str = Query(None)
-class Admin(BaseModel):
- # users
- user: str
- admin_users: Optional[str]
- allowed_users: Optional[str]
- admin_ext: Optional[str]
- disabled_ext: Optional[str]
- funding_source: Optional[str]
- # ops
- data_folder: Optional[str]
- database_url: Optional[str]
- force_https: bool = Field(default=True)
- reserve_fee_min: Optional[int]
- reserve_fee_pct: Optional[float]
- service_fee: float = Optional[float]
- hide_api: bool = Field(default=False)
- # Change theme
- site_title: Optional[str]
- site_tagline: Optional[str]
- site_description: Optional[str]
- default_wallet_name: Optional[str]
- denomination: str = Field(default="sats")
- theme: Optional[str]
- custom_logo: Optional[str]
- ad_space: Optional[str]
+# class Admin(BaseModel):
+# # users
+# user: str
+# admin_users: Optional[str]
+# allowed_users: Optional[str]
+# admin_ext: Optional[str]
+# disabled_ext: Optional[str]
+# funding_source: Optional[str]
+# # ops
+# data_folder: Optional[str]
+# database_url: Optional[str]
+# force_https: bool = Field(default=True)
+# reserve_fee_min: Optional[int]
+# reserve_fee_pct: Optional[float]
+# service_fee: float = Optional[float]
+# hide_api: bool = Field(default=False)
+# # Change theme
+# site_title: Optional[str]
+# site_tagline: Optional[str]
+# site_description: Optional[str]
+# default_wallet_name: Optional[str]
+# denomination: str = Field(default="sats")
+# theme: Optional[str]
+# custom_logo: Optional[str]
+# ad_space: Optional[str]
- @classmethod
- def from_row(cls, row: Row) -> "Admin":
- data = dict(row)
- return cls(**data)
+# @classmethod
+# def from_row(cls, row: Row) -> "Admin":
+# data = dict(row)
+# return cls(**data)
class Funding(BaseModel):
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
new file mode 100644
index 00000000..2ed0aae2
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -0,0 +1,158 @@
+
+
+ Wallets Management
+
+
+
+
+
Funding Source Info
+
+ {%raw%}
+
+ Funding Source: {{data.settings.lnbits_backend_wallet_class}}
+
+ Balance: {{data.balance / 1000}} sats
+ {%endraw%}
+
+
+
+
+
+
+
+
+
Active Funding (Requires server restart)
+
+
+
+
+
+
+
+
+
+
+
TopUp a wallet
+
+
+
+
+
+
+
+
Funding Sources
+ {% raw %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_server.html b/lnbits/extensions/admin/templates/admin/_tab_server.html
new file mode 100644
index 00000000..2924e6a4
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_server.html
@@ -0,0 +1,78 @@
+
+
+ Server Management
+
+
+
+
+
Server Info
+
+ {%raw%}
+
+ SQlite: {{data.settings.lnbits_data_folder}}
+
+
+ Postgres: {{data.settings.lnbits_database_url}}
+
+ {%endraw%}
+
+
+
+
+
+
+
+
Miscelaneous
+
+
+ Force HTTPS
+ Prefer secure URLs
+
+
+
+
+
+
+
+ Hide API
+ Hides wallet api, extensions can choose to honor
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html
new file mode 100644
index 00000000..41dc0447
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html
@@ -0,0 +1,122 @@
+
+
+ UI Management
+
+
+
+
+
+
+
+
Default Wallet Name
+
+
+
+
+
+
+
+
+
Advertisement Slots
+
+
+
+
+ {% raw %}
+
+ {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html
new file mode 100644
index 00000000..3eb53ac4
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_users.html
@@ -0,0 +1,96 @@
+
+
+ User Management
+
+
+ Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{%
+ endraw %}
+
+
+
+
Admin Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
Allowed Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 319ca3f0..87e89321 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -29,1118 +29,16 @@
-
-
-
- Wallets Management
-
-
-
-
-
Funding Source Info
-
- {%raw%}
- Funding Source: {{data.admin.funding_source}}
- Balance: {{data.admin.balance / 1000}} sats
- {%endraw%}
-
-
-
-
-
-
-
-
-
- Active Funding
- (Requires server restart)
-
-
-
-
-
-
-
-
-
-
-
-
-
TopUp a wallet
-
-
-
-
-
-
-
-
-
Funding Sources
- {% raw %}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% endraw %}
-
-
-
-
-
-
- User Management
-
-
- Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %}
-
-
-
-
Admin Users
-
-
-
-
- {% raw %}
-
- {{ user }}
-
- {% endraw %}
-
-
-
-
-
Allowed Users
-
-
-
-
- {% raw %}
-
- {{ user }}
-
- {% endraw %}
-
-
-
-
-
-
-
Disabled Extensions
-
-
-
-
-
- Save
-
-
-
-
-
- Server Management
-
-
-
-
-
Server Info
-
- {%raw%}
-
- SQlite: {{data.admin.data_folder}}
-
-
- Postgres: {{data.admin.database_url}}
-
- {%endraw%}
-
-
-
-
-
-
-
-
Miscelaneous
-
-
- Force HTTPS
- Prefer secure URLs
-
-
-
-
-
-
-
- Hide API
- Hides wallet api, extensions can choose to
- honor
-
-
-
-
-
-
-
-
-
-
-
- Save
-
-
-
-
-
- UI Management
-
-
-
-
-
-
-
-
Default Wallet Name
-
-
-
-
-
-
-
-
-
Advertisement Slots
-
-
-
-
- {% raw %}
-
- {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
-
- {% endraw %}
-
-
-
-
-
-
-
-
- Save
-
-
-
+ {% include "admin/_tab_funding.html" %} {% include
+ "admin/_tab_users.html" %} {% include "admin/_tab_server.html" %} {%
+ include "admin/_tab_theme.html" %}
-
-
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html
index 0f3e0e09..3c0998b5 100644
--- a/lnbits/extensions/cashu/templates/cashu/mint.html
+++ b/lnbits/extensions/cashu/templates/cashu/mint.html
@@ -5,14 +5,17 @@
+ name="account_balance"
+ class="text-grey"
+ style="font-size: 10rem"
+ >
{{ mint_name }}
- Some data about mint here: * whether its online * Who to contact for support * etc...
+
+ Some data about mint here: * whether its online * Who to
+ contact for support * etc...
+
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index a5d5f371..75dff22e 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1,41 +1,69 @@
-{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Wallet {% endraw %}
-
-{% endblock %} {% block footer %}{% endblock %} {% block page_container %}
+{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Wallet
+{% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block
+page_container %}
-
-
-
+
- {% raw %} {{balanceAmount}}
- {{tickershort}}{% endraw %}
+
+ {% raw %} {{balanceAmount}} {{tickershort}}{%
+ endraw %}
+
-
-
-
+
- Receive
+ Receive
- Send
+ Send
- Peg in/out
+ Peg in/out
- scan
+ scan
-
-
-
+
@@ -43,42 +71,99 @@
Transactions
- {% raw %}
-
{% endraw %}
+
+ {% raw %}
+ {% endraw %}
Mint details
- Export to CSV
+ Export to CSV
-
+
Show chart
-
+
-
+
{% raw %}
- {{ col.label }}
+ {{ col.label }}
-
-
+
+
Pending
-
-
-
+
+
+
#{{ props.row.tag }}
@@ -89,7 +174,12 @@
{{ props.row.dateFrom }}
{% endraw %}
- {% raw %} {{
+ {% raw %} {{
parseFloat(String(props.row.fsat).replaceAll(",", "")) / 100
}}
@@ -108,34 +198,68 @@
Invoice waiting to be paid
-
-
+
+
- Copy invoice
- Close
+ Copy invoice
+ Close
-
+
Payment Received
-
+
-
+
Payment Sent
-
+
Outgoing payment pending
-
+
@@ -149,49 +273,101 @@
{% raw %}
-
+
{{receive.lnurl.domain}} is requesting an invoice:
{% endraw %} {% if LNBITS_DENOMINATION != 'sats' %}
-
+
{% else %}
-
-
+
+
{% endif %}
-
+
{% raw %}
-
+
Withdraw from {{receive.lnurl.domain}}
Create invoice
- Cancel
+ Cancel
-
+
- Copy invoice
- Close
+ Copy invoice
+ Close
{% endraw %}
@@ -217,11 +393,17 @@
{% endraw %}
Pay
- Cancel
+ Cancel
- Not enough funds!
- Cancel
+ Not enough funds!
+ Cancel
@@ -243,23 +425,46 @@
Login
- Cancel
+ Cancel
{% endraw %}
-
-
+
+
- Read
- Cancel
+ Read
+ Cancel
-
+
@@ -274,10 +479,15 @@
-
+
- Cancel
+ Cancel
@@ -289,12 +499,19 @@
-
+
-
+
@@ -302,8 +519,10 @@
Warning
- BOOKMARK THIS PAGE! If only mobile you can also click the 3 dots
- and "Save to homescreen"/"Install app" !
+ BOOKMARK THIS PAGE! If only mobile you can also click the 3 dots
+ and "Save to homescreen"/"Install app" !
Ecash is a bearer asset, meaning you have the funds saved on this
@@ -311,8 +530,15 @@
lose the funds.
- Copy wallet URL
- I understand
+ Copy wallet URL
+ I understand
@@ -357,397 +583,391 @@
mixins: [windowMixin],
data: function () {
return {
- balanceAmount:"",
- tickershort:"",
- name:"",
- receive: {
- show: false,
- status: 'pending',
- paymentReq: null,
- paymentHash: null,
- minMax: [0, 2100000000000000],
- lnurl: null,
- units: ['sat'],
- unit: 'sat',
- data: {
- amount: null,
- memo: ''
- }
- },
- parse: {
- show: false,
- invoice: null,
- lnurlpay: null,
- lnurlauth: null,
- data: {
- request: '',
- amount: 0,
- comment: ''
- },
- paymentChecker: null,
- camera: {
+ balanceAmount: '',
+ tickershort: '',
+ name: '',
+ receive: {
show: false,
- camera: 'auto'
- }
- },
- payments: [],
- paymentsTable: {
- columns: [
- {
- name: 'note',
- align: 'left',
- label: 'Note',
- field: 'note'
- },
- {
- name: 'date',
- align: 'left',
- label: 'Date',
- field: 'date',
- sortable: true
- },
- {
- name: 'amount',
- align: 'right',
- label: 'Amount',
- field: 'amount',
- sortable: true
+ status: 'pending',
+ paymentReq: null,
+ paymentHash: null,
+ minMax: [0, 2100000000000000],
+ lnurl: null,
+ units: ['sat'],
+ unit: 'sat',
+ data: {
+ amount: null,
+ memo: ''
}
- ],
- pagination: {
- rowsPerPage: 10
},
- filter: null
- },
- paymentsChart: {
- show: false
- },
- disclaimerDialog: {
- show: false,
- location: window.location
- },
- balance: 0,
- credit: 0,
- newName: ''
- }
+ parse: {
+ show: false,
+ invoice: null,
+ lnurlpay: null,
+ lnurlauth: null,
+ data: {
+ request: '',
+ amount: 0,
+ comment: ''
+ },
+ paymentChecker: null,
+ camera: {
+ show: false,
+ camera: 'auto'
+ }
+ },
+ payments: [],
+ paymentsTable: {
+ columns: [
+ {
+ name: 'note',
+ align: 'left',
+ label: 'Note',
+ field: 'note'
+ },
+ {
+ name: 'date',
+ align: 'left',
+ label: 'Date',
+ field: 'date',
+ sortable: true
+ },
+ {
+ name: 'amount',
+ align: 'right',
+ label: 'Amount',
+ field: 'amount',
+ sortable: true
+ }
+ ],
+ pagination: {
+ rowsPerPage: 10
+ },
+ filter: null
+ },
+ paymentsChart: {
+ show: false
+ },
+ disclaimerDialog: {
+ show: false,
+ location: window.location
+ },
+ balance: 0,
+ credit: 0,
+ newName: ''
+ }
},
computed: {
- formattedBalance: function () {
- return this.balance / 100
- },
- filteredPayments: function () {
- var q = this.paymentsTable.filter
- if (!q || q === '') return this.payments
+ formattedBalance: function () {
+ return this.balance / 100
+ },
+ filteredPayments: function () {
+ var q = this.paymentsTable.filter
+ if (!q || q === '') return this.payments
- return LNbits.utils.search(this.payments, q)
+ return LNbits.utils.search(this.payments, q)
+ },
+ canPay: function () {
+ if (!this.parse.invoice) return false
+ return this.parse.invoice.sat <= this.balance
+ },
+ pendingPaymentsExist: function () {
+ return this.payments.findIndex(payment => payment.pending) !== -1
+ }
},
- canPay: function () {
- if (!this.parse.invoice) return false
- return this.parse.invoice.sat <= this.balance
+ filters: {
+ msatoshiFormat: function (value) {
+ return LNbits.utils.formatSat(value / 1000)
+ }
},
- pendingPaymentsExist: function () {
- return this.payments.findIndex(payment => payment.pending) !== -1
- }
- },
- filters: {
- msatoshiFormat: function (value) {
- return LNbits.utils.formatSat(value / 1000)
- }
- },
- methods: {
- paymentTableRowKey: function (row) {
- return row.payment_hash + row.amount
- },
- closeCamera: function () {
- this.parse.camera.show = false
- },
- showCamera: function () {
- this.parse.camera.show = true
- },
- showChart: function () {
- this.paymentsChart.show = true
- this.$nextTick(() => {
- generateChart(this.$refs.canvas, this.payments)
- })
- },
- focusInput(el) {
- this.$nextTick(() => this.$refs[el].focus())
- },
- showReceiveDialog: function () {
- this.receive.show = true
- this.receive.status = 'pending'
- this.receive.paymentReq = null
- this.receive.paymentHash = null
- this.receive.data.amount = null
- this.receive.data.memo = null
- this.receive.unit = 'sat'
- this.receive.paymentChecker = null
- this.receive.minMax = [0, 2100000000000000]
- this.receive.lnurl = null
- this.focusInput('setAmount')
- },
- showParseDialog: function () {
- this.parse.show = true
- this.parse.invoice = null
- this.parse.lnurlpay = null
- this.parse.lnurlauth = null
- this.parse.data.request = ''
- this.parse.data.comment = ''
- this.parse.data.paymentChecker = null
- this.parse.camera.show = false
- },
- updateBalance: function (credit) {
- this.balance = this.balance // update balance
- },
- closeReceiveDialog: function () {
- setTimeout(() => {
- clearInterval(this.receive.paymentChecker)
- }, 10000)
- },
- closeParseDialog: function () {
- setTimeout(() => {
- clearInterval(this.parse.paymentChecker)
- }, 10000)
- },
- onPaymentReceived: function (paymentHash) {
- this.fetchPayments()
- this.fetchBalance()
-
- if (this.receive.paymentHash === paymentHash) {
- this.receive.show = false
+ methods: {
+ paymentTableRowKey: function (row) {
+ return row.payment_hash + row.amount
+ },
+ closeCamera: function () {
+ this.parse.camera.show = false
+ },
+ showCamera: function () {
+ this.parse.camera.show = true
+ },
+ showChart: function () {
+ this.paymentsChart.show = true
+ this.$nextTick(() => {
+ generateChart(this.$refs.canvas, this.payments)
+ })
+ },
+ focusInput(el) {
+ this.$nextTick(() => this.$refs[el].focus())
+ },
+ showReceiveDialog: function () {
+ this.receive.show = true
+ this.receive.status = 'pending'
+ this.receive.paymentReq = null
this.receive.paymentHash = null
- clearInterval(this.receive.paymentChecker)
- }
- },
- createInvoice: function () {
- this.receive.status = 'loading'
- if (LNBITS_DENOMINATION != 'sats') {
- this.receive.data.amount = this.receive.data.amount * 100
- }
- LNbits.api
- .createInvoice(
- this.receive.data.amount,
- this.receive.data.memo,
- this.receive.unit,
- this.receive.lnurl && this.receive.lnurl.callback
- )
- .then(response => {
- this.receive.status = 'success'
- this.receive.paymentReq = response.data.payment_request
- this.receive.paymentHash = response.data.payment_hash
+ this.receive.data.amount = null
+ this.receive.data.memo = null
+ this.receive.unit = 'sat'
+ this.receive.paymentChecker = null
+ this.receive.minMax = [0, 2100000000000000]
+ this.receive.lnurl = null
+ this.focusInput('setAmount')
+ },
+ showParseDialog: function () {
+ this.parse.show = true
+ this.parse.invoice = null
+ this.parse.lnurlpay = null
+ this.parse.lnurlauth = null
+ this.parse.data.request = ''
+ this.parse.data.comment = ''
+ this.parse.data.paymentChecker = null
+ this.parse.camera.show = false
+ },
+ updateBalance: function (credit) {
+ this.balance = this.balance // update balance
+ },
+ closeReceiveDialog: function () {
+ setTimeout(() => {
+ clearInterval(this.receive.paymentChecker)
+ }, 10000)
+ },
+ closeParseDialog: function () {
+ setTimeout(() => {
+ clearInterval(this.parse.paymentChecker)
+ }, 10000)
+ },
+ onPaymentReceived: function (paymentHash) {
+ this.fetchPayments()
+ this.fetchBalance()
- if (response.data.lnurl_response !== null) {
- if (response.data.lnurl_response === false) {
- response.data.lnurl_response = `Unable to connect`
+ if (this.receive.paymentHash === paymentHash) {
+ this.receive.show = false
+ this.receive.paymentHash = null
+ clearInterval(this.receive.paymentChecker)
+ }
+ },
+ createInvoice: function () {
+ this.receive.status = 'loading'
+ if (LNBITS_DENOMINATION != 'sats') {
+ this.receive.data.amount = this.receive.data.amount * 100
+ }
+ LNbits.api
+ .createInvoice(
+ this.receive.data.amount,
+ this.receive.data.memo,
+ this.receive.unit,
+ this.receive.lnurl && this.receive.lnurl.callback
+ )
+ .then(response => {
+ this.receive.status = 'success'
+ this.receive.paymentReq = response.data.payment_request
+ this.receive.paymentHash = response.data.payment_hash
+
+ if (response.data.lnurl_response !== null) {
+ if (response.data.lnurl_response === false) {
+ response.data.lnurl_response = `Unable to connect`
+ }
+
+ if (typeof response.data.lnurl_response === 'string') {
+ // failure
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
+ caption: response.data.lnurl_response
+ })
+ return
+ } else if (response.data.lnurl_response === true) {
+ // success
+ this.$q.notify({
+ timeout: 5000,
+ message: `Invoice sent to ${this.receive.lnurl.domain}!`,
+ spinner: true
+ })
+ }
}
- if (typeof response.data.lnurl_response === 'string') {
- // failure
- this.$q.notify({
- timeout: 5000,
- type: 'warning',
- message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
- caption: response.data.lnurl_response
- })
- return
- } else if (response.data.lnurl_response === true) {
- // success
- this.$q.notify({
- timeout: 5000,
- message: `Invoice sent to ${this.receive.lnurl.domain}!`,
- spinner: true
- })
+ clearInterval(this.receive.paymentChecker)
+ setTimeout(() => {
+ clearInterval(this.receive.paymentChecker)
+ }, 40000)
+ })
+ .catch(err => {
+ LNbits.utils.notifyApiError(err)
+ this.receive.status = 'pending'
+ })
+ },
+ decodeQR: function (res) {
+ this.parse.data.request = res
+ this.decodeRequest()
+ this.parse.camera.show = false
+ },
+ decodeRequest: function () {
+ this.parse.show = true
+ let req = this.parse.data.request.toLowerCase()
+ if (this.parse.data.request.toLowerCase().startsWith('lightning:')) {
+ this.parse.data.request = this.parse.data.request.slice(10)
+ } else if (this.parse.data.request.toLowerCase().startsWith('lnurl:')) {
+ this.parse.data.request = this.parse.data.request.slice(6)
+ } else if (req.indexOf('lightning=lnurl1') !== -1) {
+ this.parse.data.request = this.parse.data.request
+ .split('lightning=')[1]
+ .split('&')[0]
+ }
+
+ if (
+ this.parse.data.request.toLowerCase().startsWith('lnurl1') ||
+ this.parse.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
+ ) {
+ return
+ }
+
+ let invoice
+ try {
+ invoice = decode(this.parse.data.request)
+ } catch (error) {
+ this.$q.notify({
+ timeout: 3000,
+ type: 'warning',
+ message: error + '.',
+ caption: '400 BAD REQUEST'
+ })
+ this.parse.show = false
+ return
+ }
+
+ let cleanInvoice = {
+ msat: invoice.human_readable_part.amount,
+ sat: invoice.human_readable_part.amount / 1000,
+ fsat: LNbits.utils.formatSat(
+ invoice.human_readable_part.amount / 1000
+ )
+ }
+
+ _.each(invoice.data.tags, tag => {
+ if (_.isObject(tag) && _.has(tag, 'description')) {
+ if (tag.description === 'payment_hash') {
+ cleanInvoice.hash = tag.value
+ } else if (tag.description === 'description') {
+ cleanInvoice.description = tag.value
+ } else if (tag.description === 'expiry') {
+ var expireDate = new Date(
+ (invoice.data.time_stamp + tag.value) * 1000
+ )
+ cleanInvoice.expireDate = Quasar.utils.date.formatDate(
+ expireDate,
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ'
+ )
+ cleanInvoice.expired = false // TODO
}
}
+ })
- clearInterval(this.receive.paymentChecker)
- setTimeout(() => {
- clearInterval(this.receive.paymentChecker)
- }, 40000)
-
+ this.parse.invoice = Object.freeze(cleanInvoice)
+ },
+ payInvoice: function () {
+ let dismissPaymentMsg = this.$q.notify({
+ timeout: 0,
+ message: 'Processing payment...'
+ })
+ },
+ payLnurl: function () {
+ let dismissPaymentMsg = this.$q.notify({
+ timeout: 0,
+ message: 'Processing payment...'
+ })
+ },
+ authLnurl: function () {
+ let dismissAuthMsg = this.$q.notify({
+ timeout: 10,
+ message: 'Performing authentication...'
+ })
+ },
+
+ deleteWallet: function (walletId, user) {
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this wallet?')
+ .onOk(() => {
+ LNbits.href.deleteWallet(walletId, user)
+ })
+ },
+ fetchPayments: function () {
+ return
+ },
+ fetchBalance: function () {},
+ exportCSV: function () {
+ // status is important for export but it is not in paymentsTable
+ // because it is manually added with payment detail link and icons
+ // and would cause duplication in the list
+ let columns = this.paymentsTable.columns
+ columns.unshift({
+ name: 'pending',
+ align: 'left',
+ label: 'Pending',
+ field: 'pending'
+ })
+ LNbits.utils.exportCSV(columns, this.payments)
+ }
+ },
+ watch: {
+ payments: function () {
+ this.fetchBalance()
+ }
+ },
+ created: function () {
+ this.fetchBalance()
+ this.fetchPayments()
+
+ LNbits.api
+ .request('GET', '/api/v1/currencies')
+ .then(response => {
+ this.receive.units = ['sat', ...response.data]
})
.catch(err => {
LNbits.utils.notifyApiError(err)
- this.receive.status = 'pending'
})
},
- decodeQR: function (res) {
- this.parse.data.request = res
- this.decodeRequest()
- this.parse.camera.show = false
- },
- decodeRequest: function () {
- this.parse.show = true
- let req = this.parse.data.request.toLowerCase()
- if (this.parse.data.request.toLowerCase().startsWith('lightning:')) {
- this.parse.data.request = this.parse.data.request.slice(10)
- } else if (this.parse.data.request.toLowerCase().startsWith('lnurl:')) {
- this.parse.data.request = this.parse.data.request.slice(6)
- } else if (req.indexOf('lightning=lnurl1') !== -1) {
- this.parse.data.request = this.parse.data.request
- .split('lightning=')[1]
- .split('&')[0]
- }
+ created: function () {
+ let params = new URL(document.location).searchParams
+ // get ticker
if (
- this.parse.data.request.toLowerCase().startsWith('lnurl1') ||
- this.parse.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
+ !params.get('tsh') &&
+ !this.$q.localStorage.getItem('cashu.tickershort')
) {
- return
- }
-
- let invoice
- try {
- invoice = decode(this.parse.data.request)
- } catch (error) {
- this.$q.notify({
- timeout: 3000,
- type: 'warning',
- message: error + '.',
- caption: '400 BAD REQUEST'
- })
- this.parse.show = false
- return
- }
-
- let cleanInvoice = {
- msat: invoice.human_readable_part.amount,
- sat: invoice.human_readable_part.amount / 1000,
- fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
- }
-
- _.each(invoice.data.tags, tag => {
- if (_.isObject(tag) && _.has(tag, 'description')) {
- if (tag.description === 'payment_hash') {
- cleanInvoice.hash = tag.value
- } else if (tag.description === 'description') {
- cleanInvoice.description = tag.value
- } else if (tag.description === 'expiry') {
- var expireDate = new Date(
- (invoice.data.time_stamp + tag.value) * 1000
- )
- cleanInvoice.expireDate = Quasar.utils.date.formatDate(
- expireDate,
- 'YYYY-MM-DDTHH:mm:ss.SSSZ'
- )
- cleanInvoice.expired = false // TODO
- }
- }
- })
-
- this.parse.invoice = Object.freeze(cleanInvoice)
- },
- payInvoice: function () {
- let dismissPaymentMsg = this.$q.notify({
- timeout: 0,
- message: 'Processing payment...'
- })
- },
- payLnurl: function () {
- let dismissPaymentMsg = this.$q.notify({
- timeout: 0,
- message: 'Processing payment...'
- })
- },
- authLnurl: function () {
- let dismissAuthMsg = this.$q.notify({
- timeout: 10,
- message: 'Performing authentication...'
- })
- },
-
- deleteWallet: function (walletId, user) {
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this wallet?')
- .onOk(() => {
- LNbits.href.deleteWallet(walletId, user)
- })
- },
- fetchPayments: function () {
- return
- },
- fetchBalance: function () {
-
- },
- exportCSV: function () {
- // status is important for export but it is not in paymentsTable
- // because it is manually added with payment detail link and icons
- // and would cause duplication in the list
- let columns = this.paymentsTable.columns
- columns.unshift({
- name: 'pending',
- align: 'left',
- label: 'Pending',
- field: 'pending'
- })
- LNbits.utils.exportCSV(columns, this.payments)
- }
- },
- watch: {
- payments: function () {
- this.fetchBalance()
- }
- },
- created: function () {
- this.fetchBalance()
- this.fetchPayments()
-
- LNbits.api
- .request('GET', '/api/v1/currencies')
- .then(response => {
- this.receive.units = ['sat', ...response.data]
- })
- .catch(err => {
- LNbits.utils.notifyApiError(err)
- })
- },
- created: function () {
-
- let params = (new URL(document.location)).searchParams
-
- // get ticker
- if(!params.get('tsh') && !this.$q.localStorage.getItem('cashu.tickershort')){
- this.$q.localStorage.set('cashu.tickershort', "CE")
- this.tickershort = "CE"
- }
- else if(params.get('tsh')){
+ this.$q.localStorage.set('cashu.tickershort', 'CE')
+ this.tickershort = 'CE'
+ } else if (params.get('tsh')) {
this.$q.localStorage.set('cashu.tickershort', params.get('tsh'))
this.tickershort = params.get('tsh')
- }
- else if(this.$q.localStorage.getItem('cashu.tickershort')){
+ } else if (this.$q.localStorage.getItem('cashu.tickershort')) {
this.tickershort = this.$q.localStorage.getItem('cashu.tickershort')
- }
+ }
-
- if (!this.$q.localStorage.getItem('cashu.amount')) {
- this.balanceAmount = 0
- }
+ if (!this.$q.localStorage.getItem('cashu.amount')) {
+ this.balanceAmount = 0
+ }
- // get mint
- if (params.get('mnt')) {
- this.mint = params.get('mnt')
- this.$q.localStorage.set('cashu.mint', params.get('mnt'))
- }
- else if(this.$q.localStorage.getItem('cashu.mint')){
- this.mint = this.$q.localStorage.getItem('cashu.mint')
- }
- else{
- this.$q.notify({
- color: 'red',
- message: 'No mint set!'
- })
- }
+ // get mint
+ if (params.get('mnt')) {
+ this.mint = params.get('mnt')
+ this.$q.localStorage.set('cashu.mint', params.get('mnt'))
+ } else if (this.$q.localStorage.getItem('cashu.mint')) {
+ this.mint = this.$q.localStorage.getItem('cashu.mint')
+ } else {
+ this.$q.notify({
+ color: 'red',
+ message: 'No mint set!'
+ })
+ }
- // get name
- if (params.get('nme')) {
- this.name = params.get('nme')
- this.$q.localStorage.set('cashu.name', params.get('nme'))
+ // get name
+ if (params.get('nme')) {
+ this.name = params.get('nme')
+ this.$q.localStorage.set('cashu.name', params.get('nme'))
+ } else if (this.$q.localStorage.getItem('cashu.name')) {
+ this.name = this.$q.localStorage.getItem('cashu.name')
+ }
}
- else if(this.$q.localStorage.getItem('cashu.name')){
- this.name = this.$q.localStorage.getItem('cashu.name')
- }
-
- }
-})
+ })
{% endblock %}
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 655ed028..097fbd00 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -22,14 +22,19 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
"cashu/index.html", {"request": request, "user": user.dict()}
)
+
@cashu_ext.get("/wallet")
async def cashu(request: Request):
- return cashu_renderer().TemplateResponse("cashu/wallet.html",{"request": request})
+ return cashu_renderer().TemplateResponse("cashu/wallet.html", {"request": request})
+
@cashu_ext.get("/mint/{mintID}")
async def cashu(request: Request, mintID):
cashu = await get_cashu(mintID)
- return cashu_renderer().TemplateResponse("cashu/mint.html",{"request": request, "mint_name": cashu.name})
+ return cashu_renderer().TemplateResponse(
+ "cashu/mint.html", {"request": request, "mint_name": cashu.name}
+ )
+
@cashu_ext.get("/manifest/{cashu_id}.webmanifest")
async def manifest(cashu_id: str):
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index c0bc59f3..ab96e101 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -1,5 +1,4 @@
from http import HTTPStatus
-from secp256k1 import PublicKey
from typing import Union
import httpx
@@ -7,35 +6,36 @@ from fastapi import Query
from fastapi.params import Depends
from lnurl import decode as decode_lnurl
from loguru import logger
+from secp256k1 import PublicKey
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
-from lnbits.core.services import create_invoice
+from lnbits.core.services import check_transaction_status, create_invoice
from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from .core.base import CashuError
+from lnbits.wallets.base import PaymentStatus
from . import cashu_ext
-from .ledger import request_mint, mint
-from .mint import get_pubkeys
-
+from .core.base import CashuError
from .crud import (
- create_cashu,
- delete_cashu,
- get_cashu,
+ create_cashu,
+ delete_cashu,
+ get_cashu,
get_cashus,
+ get_lightning_invoice,
store_lightning_invoice,
)
-
+from .ledger import mint, request_mint
+from .mint import get_pubkeys
from .models import (
Cashu,
- Invoice,
- Pegs,
- CheckPayload,
- MeltPayload,
- MintPayloads,
- SplitPayload,
- PayLnurlWData
+ CheckPayload,
+ Invoice,
+ MeltPayload,
+ MintPayloads,
+ PayLnurlWData,
+ Pegs,
+ SplitPayload,
)
########################################
@@ -55,13 +55,12 @@ async def api_cashus(
@cashu_ext.post("/api/v1/cashus", 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)):
cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
logger.debug(cashu)
return cashu.dict()
+
@cashu_ext.post("/api/v1/cashus/upodatekeys", status_code=HTTPStatus.CREATED)
async def api_cashu_update_keys(
data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
@@ -122,7 +121,8 @@ async def api_cashu_create_invoice(
@cashu_ext.post(
- "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay", status_code=HTTPStatus.OK
+ "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay",
+ status_code=HTTPStatus.OK,
)
async def api_cashu_pay_invoice(
lnurl_data: PayLnurlWData, payment_request: str = None, cashu_id: str = None
@@ -203,26 +203,29 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
#################MINT###################
########################################
-@cashu_ext.get("/api/v1/mint/keys/{cashu_id}", status_code=HTTPStatus.OK)
-async def keys(cashu_id: str = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)):
+
+@cashu_ext.get("/api/v1/mint/keys/{cashu_id}", status_code=HTTPStatus.OK)
+async def keys(
+ cashu_id: str = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
+):
"""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."
- )
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+ )
return get_pubkeys(mint.prvkey)
@cashu_ext.get("/api/v1/mint/{cashu_id}")
async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
"""Request minting of tokens. Server responds with a Lightning invoice."""
- print('############################ amount', amount)
+
cashu = await get_cashu(cashu_id)
if cashu is None:
raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
- )
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+ )
try:
payment_hash, payment_request = await create_invoice(
@@ -237,20 +240,43 @@ async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
await store_lightning_invoice(cashu_id, invoice)
except Exception as e:
logger.error(e)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(cashu_id))
+ raise HTTPException(
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(cashu_id)
+ )
return {"pr": payment_request, "hash": payment_hash}
@cashu_ext.post("/mint")
-async def mint_coins(payloads: MintPayloads, payment_hash: Union[str, None] = None, cashu_id: str = Query(None)):
+async def mint_coins(
+ payloads: MintPayloads,
+ payment_hash: Union[str, None] = None,
+ cashu_id: str = Query(None),
+):
"""
Requests the minting of tokens belonging to a paid payment request.
-
Call this endpoint after `GET /mint`.
"""
- amounts = []
- B_s = []
+ print("############################ amount")
+ cashu: Cashu = await get_cashu(cashu_id)
+ if cashu is None:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+ )
+ invoice: Invoice = get_lightning_invoice(cashu_id, payment_hash)
+ if invoice is None:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not have this invoice."
+ )
+
+ status: PaymentStatus = check_transaction_status(cashu.wallet, payment_hash)
+ if status.paid == False:
+ raise HTTPException(
+ status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
+ )
+
+ # amounts = []
+ # B_s = []
# for payload in payloads.blinded_messages:
# amounts.append(payload.amount)
# B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
@@ -290,4 +316,4 @@ async def spli_coinst(payload: SplitPayload, cashu_id: str = Query(None)):
"""There was a problem with the split"""
raise Exception("could not split tokens.")
fst_promises, snd_promises = split_return
- return {"fst": fst_promises, "snd": snd_promises}
\ No newline at end of file
+ return {"fst": fst_promises, "snd": snd_promises}
From b27d31b381561e09024767fbcf89fe85c3bc935d Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 11:18:17 +0300
Subject: [PATCH 0101/1058] feat: add wallet check
---
lnbits/extensions/cashu/views_api.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index ab96e101..36a9d105 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -218,7 +218,11 @@ async def keys(
@cashu_ext.get("/api/v1/mint/{cashu_id}")
-async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
+async def mint_pay_request(
+ amount: int = 0,
+ cashu_id: str = Query(None),
+ wallet: WalletTypeInfo = Depends(get_key_type),
+):
"""Request minting of tokens. Server responds with a Lightning invoice."""
cashu = await get_cashu(cashu_id)
@@ -252,6 +256,7 @@ async def mint_coins(
payloads: MintPayloads,
payment_hash: Union[str, None] = None,
cashu_id: str = Query(None),
+ wallet: WalletTypeInfo = Depends(get_key_type),
):
"""
Requests the minting of tokens belonging to a paid payment request.
From 95ebd179ad896cf77db523c9ce9fb4ffd1aa42c2 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 11:47:37 +0300
Subject: [PATCH 0102/1058] feat: check paid invoices
---
lnbits/extensions/cashu/core/base.py | 9 +++++----
lnbits/extensions/cashu/crud.py | 4 ++--
lnbits/extensions/cashu/models.py | 7 ++++++-
lnbits/extensions/cashu/views_api.py | 20 +++++++++++++-------
4 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/lnbits/extensions/cashu/core/base.py b/lnbits/extensions/cashu/core/base.py
index 0aa02442..4943ee24 100644
--- a/lnbits/extensions/cashu/core/base.py
+++ b/lnbits/extensions/cashu/core/base.py
@@ -87,10 +87,11 @@ class Invoice(BaseModel):
@classmethod
def from_row(cls, row: Row):
return cls(
- amount=int(row[0]),
- pr=str(row[1]),
- hash=str(row[2]),
- issued=bool(row[3]),
+ cashu_id=str(row[0]),
+ amount=int(row[1]),
+ pr=str(row[2]),
+ hash=str(row[3]),
+ issued=bool(row[4]),
)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 448614ac..733f4737 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -157,7 +157,7 @@ async def store_lightning_invoice(cashu_id: str, invoice: Invoice):
async def get_lightning_invoice(cashu_id: str, hash: str):
row = await db.fetchone(
"""
- SELECT * from invoices
+ SELECT * from cashu.invoices
WHERE cashu_id =? AND hash = ?
""",
(
@@ -170,7 +170,7 @@ async def get_lightning_invoice(cashu_id: str, hash: str):
async def update_lightning_invoice(cashu_id: str, hash: str, issued: bool):
await db.execute(
- "UPDATE invoices SET issued = ? WHERE cashu_id = ? AND hash = ?",
+ "UPDATE cashu.invoices SET issued = ? WHERE cashu_id = ? AND hash = ?",
(
issued,
cashu_id,
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 570387a2..8b5a3417 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -1,5 +1,5 @@
from sqlite3 import Row
-from typing import List, Optional
+from typing import List, Union
from fastapi import Query
from pydantic import BaseModel
@@ -146,3 +146,8 @@ class MeltPayload(BaseModel):
proofs: List[Proof]
amount: int
invoice: str
+
+class CreateTokens(BaseModel):
+ # cashu_id: str = Query(None)
+ payloads: MintPayloads
+ payment_hash: Union[str, None] = None
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 36a9d105..6f1e161a 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -30,6 +30,7 @@ from .mint import get_pubkeys
from .models import (
Cashu,
CheckPayload,
+ CreateTokens,
Invoice,
MeltPayload,
MintPayloads,
@@ -251,12 +252,11 @@ async def mint_pay_request(
return {"pr": payment_request, "hash": payment_hash}
-@cashu_ext.post("/mint")
+@cashu_ext.post("/api/v1/mint/{cashu_id}")
async def mint_coins(
- payloads: MintPayloads,
- payment_hash: Union[str, None] = None,
+ data: CreateTokens,
cashu_id: str = Query(None),
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
):
"""
Requests the minting of tokens belonging to a paid payment request.
@@ -268,14 +268,20 @@ async def mint_coins(
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
)
- invoice: Invoice = get_lightning_invoice(cashu_id, payment_hash)
+ invoice: Invoice = (
+ None
+ if data.payment_hash == None
+ else await get_lightning_invoice(cashu_id, data.payment_hash)
+ )
if invoice is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not have this invoice."
)
+ # if invoice.issued == True:
+ # todo: give old tokens?
- status: PaymentStatus = check_transaction_status(cashu.wallet, payment_hash)
- if status.paid == False:
+ status: PaymentStatus = await check_transaction_status(cashu.wallet, data.payment_hash)
+ if status.paid != True:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
From a32a6da1581b06a2c6794a3c011c3acd85b997c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Sat, 1 Oct 2022 11:33:58 +0200
Subject: [PATCH 0103/1058] add warning for voidwallet for better coloring in
logs
---
lnbits/wallets/void.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/wallets/void.py b/lnbits/wallets/void.py
index 0de387aa..b74eb245 100644
--- a/lnbits/wallets/void.py
+++ b/lnbits/wallets/void.py
@@ -23,7 +23,7 @@ class VoidWallet(Wallet):
raise Unsupported("")
async def status(self) -> StatusResponse:
- logger.info(
+ logger.warning(
"This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits."
)
return StatusResponse(None, 0)
From 4c7ec4c9e4b30f2956b8cc5eef546d87a5407684 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 6 Oct 2022 09:42:38 +0200
Subject: [PATCH 0104/1058] fix poetry lock in main
---
poetry.lock | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/poetry.lock b/poetry.lock
index bb39aafc..5b283d75 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -258,6 +258,24 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "environs"
+version = "9.3.3"
+description = "simplified environment variable parsing"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+marshmallow = ">=3.0.0"
+python-dotenv = "*"
+
+[package.extras]
+dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
+django = ["dj-database-url", "dj-email-url", "django-cache-url"]
+lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
+
[[package]]
name = "fastapi"
version = "0.78.0"
@@ -1033,7 +1051,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "e798b36b5941b43ee249bc196fcfb28d8ee712947336d21467651c672ba0106b"
+content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
[metadata.files]
aiofiles = [
@@ -1291,6 +1309,10 @@ enum34 = [
{file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"},
{file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"},
]
+environs = [
+ {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
+ {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
+]
fastapi = [
{file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
{file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
From 017d73ec3e745a7701f6cac2db23595b78a0af5c Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 6 Oct 2022 17:41:06 +0100
Subject: [PATCH 0105/1058] Added code sample for websocket stuff
---
docs/devs/websockets.md | 87 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 docs/devs/websockets.md
diff --git a/docs/devs/websockets.md b/docs/devs/websockets.md
new file mode 100644
index 00000000..34cf940d
--- /dev/null
+++ b/docs/devs/websockets.md
@@ -0,0 +1,87 @@
+---
+layout: default
+parent: For developers
+title: Websockets
+nav_order: 2
+---
+
+
+Websockets
+=================
+
+`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from teh copilot extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
+
+
+```sh
+from fastapi import Request, WebSocket, WebSocketDisconnect
+
+class ConnectionManager:
+ def __init__(self):
+ self.active_connections: List[WebSocket] = []
+
+ async def connect(self, websocket: WebSocket, extension_id: str):
+ await websocket.accept()
+ websocket.id = extension_id
+ self.active_connections.append(websocket)
+
+ def disconnect(self, websocket: WebSocket):
+ self.active_connections.remove(websocket)
+
+ async def send_personal_message(self, message: str, extension_id: str):
+ for connection in self.active_connections:
+ if connection.id == extension_id:
+ await connection.send_text(message)
+
+ async def broadcast(self, message: str):
+ for connection in self.active_connections:
+ await connection.send_text(message)
+
+
+manager = ConnectionManager()
+
+
+@extension_ext.websocket("/ws/{extension_id}", name="extension.websocket_by_id")
+async def websocket_endpoint(websocket: WebSocket, extension_id: str):
+ await manager.connect(websocket, extension_id)
+ try:
+ while True:
+ data = await websocket.receive_text()
+ except WebSocketDisconnect:
+ manager.disconnect(websocket)
+
+
+async def updater(extension_id, data):
+ extension = await get_extension(extension_id)
+ if not extension:
+ return
+ await manager.send_personal_message(f"{data}", extension_id)
+```
+
+Example vue-js function for listening to the websocket:
+
+```
+initWs: async function () {
+ if (location.protocol !== 'http:') {
+ localUrl =
+ 'wss://' +
+ document.domain +
+ ':' +
+ location.port +
+ '/extension/ws/' +
+ self.extension.id
+ } else {
+ localUrl =
+ 'ws://' +
+ document.domain +
+ ':' +
+ location.port +
+ '/extension/ws/' +
+ self.extension.id
+ }
+ this.ws = new WebSocket(localUrl)
+ this.ws.addEventListener('message', async ({data}) => {
+ const res = JSON.parse(data.toString())
+ console.log(res)
+ })
+},
+```
\ No newline at end of file
From 9a1b689917e467cc5f86cf320dea6de0a21d3426 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Thu, 6 Oct 2022 17:44:04 +0100
Subject: [PATCH 0106/1058] Update websockets.md
---
docs/devs/websockets.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/devs/websockets.md b/docs/devs/websockets.md
index 34cf940d..0638e4f2 100644
--- a/docs/devs/websockets.md
+++ b/docs/devs/websockets.md
@@ -9,7 +9,7 @@ nav_order: 2
Websockets
=================
-`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from teh copilot extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
+`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from the `copilot` extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
```sh
@@ -84,4 +84,4 @@ initWs: async function () {
console.log(res)
})
},
-```
\ No newline at end of file
+```
From f09108bc0307e29ae481ce89d088f8f7610a58d7 Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Wed, 5 Oct 2022 09:16:54 +0200
Subject: [PATCH 0107/1058] updated poetry install notes
- referenced option for python3.10 and newer
- poetry install with --only main, instead of --no-dev (deprecated)
---
docs/guide/installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 87679ed5..5e9fdb28 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -26,8 +26,8 @@ sudo apt install python3.9 python3.9-distutils
curl -sSL https://install.python-poetry.org | python3 -
export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal
-poetry env use python3.9
-poetry install --no-dev
+poetry env use python3.9 # you can exchange with python3.10 or newer versions. Identify your version with python3 --version and specify here
+poetry install --only main
poetry run python build.py
mkdir data
From f72551aa4a47c4aafd1f325de5e23652bab1631c Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Thu, 6 Oct 2022 14:25:14 +0200
Subject: [PATCH 0108/1058] changed comment places and small adjustments
- #comments in own lines
- cleaned up some ominous settings
- added --debug option for running the server
---
docs/guide/installation.md | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 5e9fdb28..37104fde 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -18,21 +18,25 @@ If you have problems installing LNbits using these instructions, please have a l
git clone https://github.com/lnbits/lnbits-legend.git
cd lnbits-legend/
-# for making sure python 3.9 is installed, skip if installed
+# for making sure python 3.9 is installed, skip if installed. To check your installed version: python3 --version
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.9 python3.9-distutils
curl -sSL https://install.python-poetry.org | python3 -
-export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal
-poetry env use python3.9 # you can exchange with python3.10 or newer versions. Identify your version with python3 --version and specify here
+# Once the above poetry install is completed, use the installation path printed to terminal and replace in the following command
+export PATH="/home/user/.local/bin:$PATH"
+# Next command, you can exchange with python3.10 or newer versions.
+# Identify your version with python3 --version and specify in the next line
+# command is only needed when your default python is not ^3.9 or ^3.10
+poetry env use python3.9
poetry install --only main
-poetry run python build.py
mkdir data
cp .env.example .env
-nano .env # set funding source
+# set funding source amongst other options
+nano .env
```
#### Running the server
@@ -40,6 +44,7 @@ nano .env # set funding source
```sh
poetry run lnbits
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
+# add --debug for slightly more verbose output
```
## Option 2: Nix
From 5405493fc6cdf51f71cfe59bc2700c82b0f8c63f Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Thu, 6 Oct 2022 14:50:33 +0200
Subject: [PATCH 0109/1058] added debug option for .env
---
docs/guide/installation.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 37104fde..6b95f93b 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -44,7 +44,8 @@ nano .env
```sh
poetry run lnbits
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
-# add --debug for slightly more verbose output
+# adding --debug in the start-up command above to help your troubleshooting and generate a more verbose output
+# Note that you have to add the line DEBUG=true in your .env file, too.
```
## Option 2: Nix
From f7ca9d1c8242c7d99a605a4b7a843378d8198f3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 6 Oct 2022 10:17:21 +0200
Subject: [PATCH 0110/1058] make workflows only run on pull_request
---
.github/workflows/codeql.yml | 2 --
.github/workflows/formatting.yml | 2 --
.github/workflows/mypy.yml | 2 +-
.github/workflows/regtest.yml | 2 +-
.github/workflows/tests.yml | 2 +-
5 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 876c8b8a..bbe95983 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,8 +1,6 @@
name: codeql
on:
- push:
- branches: [main, ]
pull_request:
branches: [main]
schedule:
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
index e3d0fd35..21c7fb38 100644
--- a/.github/workflows/formatting.yml
+++ b/.github/workflows/formatting.yml
@@ -1,8 +1,6 @@
name: formatting
on:
- push:
- branches: [ main ]
pull_request:
branches: [ main ]
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index d80da678..96b574d2 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -1,6 +1,6 @@
name: mypy
-on: [push, pull_request]
+on: [pull_request]
jobs:
check:
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index 2d7aae6b..f0adb3ac 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -1,6 +1,6 @@
name: regtest
-on: [push, pull_request]
+on: [pull_request]
jobs:
LndRestWallet:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 5d368fbb..c7b6e44b 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,6 +1,6 @@
name: tests
-on: [push, pull_request]
+on: [pull_request]
jobs:
venv-sqlite:
From ba74d0ef7aadf1c826e09b658f96d3ae9186d2e5 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:21:09 +0100
Subject: [PATCH 0111/1058] fix issue with splitting to multiple wallets-queue
---
lnbits/extensions/splitpayments/tasks.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py
index b7cf1750..cfc6c226 100644
--- a/lnbits/extensions/splitpayments/tasks.py
+++ b/lnbits/extensions/splitpayments/tasks.py
@@ -28,6 +28,10 @@ async def on_invoice_paid(payment: Payment) -> None:
# now we make some special internal transfers (from no one to the receiver)
targets = await get_targets(payment.wallet_id)
+
+ if not targets:
+ return
+
transfers = [
(target.wallet, int(target.percent * payment.amount / 100))
for target in targets
@@ -41,9 +45,6 @@ async def on_invoice_paid(payment: Payment) -> None:
)
return
- if not targets:
- return
-
# mark the original payment with one extra key, "splitted"
# (this prevents us from doing this process again and it's informative)
# and reduce it by the amount we're going to send to the producer
@@ -76,5 +77,5 @@ async def on_invoice_paid(payment: Payment) -> None:
)
# manually send this for now
- await internal_invoice_queue.put(internal_checking_id)
+ await internal_invoice_queue.put(internal_checking_id)
return
From 4a9843d1340cca7b8f3a12be3de6e79b2e41ad05 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:21:53 +0100
Subject: [PATCH 0112/1058] floor amount when not whole sat
---
lnbits/extensions/scrub/tasks.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/scrub/tasks.py b/lnbits/extensions/scrub/tasks.py
index 320d34da..62adc5e5 100644
--- a/lnbits/extensions/scrub/tasks.py
+++ b/lnbits/extensions/scrub/tasks.py
@@ -1,6 +1,7 @@
import asyncio
import json
from http import HTTPStatus
+from math import floor
from urllib.parse import urlparse
import httpx
@@ -26,7 +27,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
# (avoid loops)
- if "scrubed" == payment.extra.get("tag"):
+ if payment.extra.get("tag") == "scrubed":
# already scrubbed
return
@@ -42,12 +43,13 @@ async def on_invoice_paid(payment: Payment) -> None:
# I REALLY HATE THIS DUPLICATION OF CODE!! CORE/VIEWS/API.PY, LINE 267
domain = urlparse(data["callback"]).netloc
-
+ rounded_amount = floor(payment.amount / 1000) * 1000
+
async with httpx.AsyncClient() as client:
try:
r = await client.get(
data["callback"],
- params={"amount": payment.amount},
+ params={"amount": rounded_amount},
timeout=40,
)
if r.is_error:
@@ -66,7 +68,8 @@ async def on_invoice_paid(payment: Payment) -> None:
)
invoice = bolt11.decode(params["pr"])
- if invoice.amount_msat != payment.amount:
+
+ if invoice.amount_msat != rounded_amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected {payment.amount} msat, got {invoice.amount_msat}.",
From 933ac7469ca0f712783971bebdabf820a6d630ed Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:33:20 +0100
Subject: [PATCH 0113/1058] add disclaimer about whole (integer) sats
---
lnbits/extensions/scrub/README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/extensions/scrub/README.md b/lnbits/extensions/scrub/README.md
index 680c5e6d..3b8d0b2d 100644
--- a/lnbits/extensions/scrub/README.md
+++ b/lnbits/extensions/scrub/README.md
@@ -4,6 +4,8 @@
SCRUB is a small but handy extension that allows a user to take advantage of all the functionalities inside **LNbits** and upon a payment received to your LNbits wallet, automatically forward it to your desired wallet via LNURL or LNAddress!
+Only whole values, integers, are Scrubbed, amounts will be rounded down (example: 6.3 will be 6)! The decimals, if existing, will be kept in your wallet!
+
[**Wallets supporting LNURL**](https://github.com/fiatjaf/awesome-lnurl#wallets)
## Usage
From 154d410854a8fe2d9db433e98bf46c54bcfa6c97 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:34:37 +0100
Subject: [PATCH 0114/1058] make format
---
lnbits/extensions/scrub/tasks.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/scrub/tasks.py b/lnbits/extensions/scrub/tasks.py
index 62adc5e5..852f3860 100644
--- a/lnbits/extensions/scrub/tasks.py
+++ b/lnbits/extensions/scrub/tasks.py
@@ -44,7 +44,7 @@ async def on_invoice_paid(payment: Payment) -> None:
# I REALLY HATE THIS DUPLICATION OF CODE!! CORE/VIEWS/API.PY, LINE 267
domain = urlparse(data["callback"]).netloc
rounded_amount = floor(payment.amount / 1000) * 1000
-
+
async with httpx.AsyncClient() as client:
try:
r = await client.get(
@@ -68,7 +68,7 @@ async def on_invoice_paid(payment: Payment) -> None:
)
invoice = bolt11.decode(params["pr"])
-
+
if invoice.amount_msat != rounded_amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
From 1ae208c7cb58515ae8094edd8cb4bce190e7e797 Mon Sep 17 00:00:00 2001
From: Taylor Helsper
Date: Thu, 29 Sep 2022 23:06:45 -0500
Subject: [PATCH 0115/1058] Update index.html
---
lnbits/core/templates/core/index.html | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index 03cf706f..aa0398cf 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -171,6 +171,19 @@
+
From bf3488fdf4b75046ea11f331d992b9b3895ed4e2 Mon Sep 17 00:00:00 2001
From: Taylor Helsper
Date: Thu, 29 Sep 2022 23:42:38 -0500
Subject: [PATCH 0116/1058] Update myNode images
---
lnbits/static/images/mynode.png | Bin 3757 -> 13994 bytes
lnbits/static/images/mynodel.png | Bin 14352 -> 9272 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/lnbits/static/images/mynode.png b/lnbits/static/images/mynode.png
index cf25bc586797b750a64ddba848b2499a0698a5b2..390446b8ee906da9e01142b477a59506269ce5e7 100644
GIT binary patch
literal 13994
zcmeAS@N?(olHy`uVBq!ia0y~yU^v0Rz!1g3!oa`~{{Lbu1B2MQs*s41pu}>8f};Gi
z%$!t(lFEWqh1817GzNx>TUTdA7O7a4x&GhCrORmiVOsM=AL|RBO7)sjLer++idy?m
zaHgcA)!l~&q~9^{m;ZZhzy3G>KChKGpI)lk8f6=QbJNj-{CDg3J*(!m+kgH0)_woQ
z>TKVg`ti&8Nx&o9+PbYjO8*qhnSc2D`){TJKkWWjFL*D1=)v+&$K{V*=RNf2>yJND
z2H)fKF<+vV=~wZC-{0Q{D;IaEw?3)t)vQaK`Ne+O!QCIFCR@&(A+&!<
zxyJH8Oz%BagAV9*&uI*m)>F$sCA3OKvKi>WOq4@S&=k%?zK6STx^yQyOY!}S4cwgIk
zRHWEu<43v2T`y%{KRNVB>96BVzniVn(w+BL2D5cXdCK(J7jwSf_bqR`)AD!a56(zP
z=$!xL@q$yvN0e_iIoxzgT$WY}yyT|7-qgPhYRt(n99^m0-8
zl?xgxpD=xx7FM;rR@B@<*yGfQf2oMUo5|N
z_DzX9B?g}!ygDeEe8%GPnayXcZoj$wEMm&}Q>)kPy7lVxwHt-%+cTfUmfx}j(ZIolUV$sNspKQP3b+n
zww7OzMcvg>IoXii=-w1YE-S6{eeH7=8=1e9Vt;=@RMqJ8q9<<_^h`WhT*Kc+*;QxBb<-orZ9>LlEhx2%!HVACrR+uGg&p>cdxYYC{~`+027a6W_c&CfBRAWYOn{%}ZS(zim<5+jL}?R`{zmsqxQe
z?u$_>(0Q$YX40nYkW>fG9nO-6BdXQAd|Qv#W`5pTrhR6G=!T^0g~C(q-DX~Uy;30D
zK6~lRMROmQ_HI7BmalYv_UoTIvO2YKKUC5lh{y4)HVl&3zP{{lPU@yFRytR9PIO+v
zJgH>qIR?pdTf#NW{zg{*ic*fPe|aUE^E>aqs&~RKZcB@GciiR+viP#k%izGmU753+
zb~v1$H~CNesfSw%6s{hNNLuH5-|?A;?yS3diWaPICq(Ri`#Ly!!HjJ6HLqX(7uyjX
z7C+5QS@C|zo2d(I=N)==V*g#AnYsGcG>ttN_0Qh7TYbA&Rq8(5>szZfSh?pZ>RGF7
zQki(!Ec$zk(f@-ychkFO%k&<2G&4PrjeB8M_sYf1x<%&RBIis)9xx@Z6u!L6Nj_b$
ztwe^m%--~bZI)_4b=bmNI?L}*IoCJ0^h*!N&W-z~m|wVg&C|@!Q+uLUcHYz4q=h%$
z^~^oRe&<@4=?#y`73oo{-x+ReEnZo2L4V_+2j}P3UQXDM?@+ZMtMM$?iTng(*{D73
zSEOvKBi;l(FDu$I!})dhw-J)*}?USZZsj{TuMv3m(iNYYuC%%3qcOB*{U
zs7$`ZAwAF1$ZzNJvv+3&Kh*jd?<@a1NzFW6byiz{)&9nxtN69MIcLr?l6d^ga?Llt
z+T$T}TO-BS&N-RLr1@KxMP#1!t`+^NVGFag*M_Law5KGToaWgl!>Q{x`O9OW(1iS`
zUQ@HFcM}_&bDL&w3w6Hm<5jBaKh;ADGtC^FwG8`b&oo)^N-110T>sPGTFdNs2EYA^
z+a{d(Ao=r2im=7f>*v0jM&Ig_|76Bx*^}o`aNly9lyaDPd(;(qNy&%0IqS<-&neWj
zc(yh1QetvJne)cn$
zjRj7pySbJWF+866iRm0;^&_L_YfMGoCFs~IX-Tb|F*U6@lkZO|Ut#mYnYqUmJ)P1O
z5~Qrke@?Rg>$dWWh&m5f>Rof^5BmZye^%HW)txLDU!LJMab3ubTNkf;l&UFg;Xe0@
zW0~v19T_G+^pDkMEuWIKrAu*PT8`Ar#hb1latw?=@@|>~Ps85~W}9V~Z!des_j+mN
z#Py-;7V3)JUZ*X#^Rnq`o9Vaf1XZ5-q&kT$IG~#QKxKl5azXDajfL$gGvy*p_g?ze
z;_O@e%!1!-L2X?$$JX9d=9pU#L>9{V)~Z*0dGI*)-}ObRi*?q_Vh-bc#>Lz8K4oiO
zX0y+O4-9x(p=@RdSSfv|Dc-_+YbIeXjAPPCCYAT{qjVBYtHWr@mqX)Z{1`F
z%xCCP>bCIgX-a#N)_WwrK_JeZn}ww(vhZa8)3*+**0Ua*UfI%RtR(noC3l#U)xka0
zm!EOZWw`k}w<~TjSH|vn`3yIf6*@n-w7&L%S4ek=dX?&dSh)lD>lVl))Taveh8{m9
z9%A!B_vQlji~R2l&ih?dy#Gt_qgaIYHr^);iw`*-ZvAXjUe9s%cfMEhyZ$mkLHD3P
z`5XUEcS$z?erkssdJue|3-7jQIubhGx_J7p8l@_R<5$WQ)>_a9L+u1Mh+t9lookK45q(AK5nR_B+niv*i{D
zDe$djN<7Gz@FZ~Zd&j>bucW`Zzmjs=#AdKkKS5M+R)oJ@-?|jF240>&R}{6xb_fJK
zJL0j-fZa26S4z?%?auig;x|_N?$Di*@x%7|(s!2{W^KGtyYTGehWbAY=DBZOdM~I+
zJGluT553@d!u{t09bYLao{1MG`-!bl-6b5E8D-sPR;%=e=D*OEO}
zE*XcTEA5ipPd`g?&|T}ka9v~3GN%=}IuJeyimxx82nn~
zmti2hSS_?>u~*>3k8iEYa+$T7ERPv>rnBsLa^Rx#yO5V*EV(C)gnU>g_b$J7&FH6-
z3&)qjSB#|xVwUWgQ0un7;bX{KrN3v+`*O9-sz`Q;I=A?~qiW~}P0O5D@>kBY?{Z-O
z+Uw(aRHd)s%fyLn5~^z4@Z}3F{+?BcjiU6a1-dG4BXrN&hhJCYxshXgLju@Wg|b%o?tdLe$}@&<%Uylyl&sRH#5f3
zRa~>`w89@XO&!6%2X36ctlw^YN5}g>=RqgM;D^kw7WK0oblMZHKXdinYdWmX%zd^W
z*9bkCQClzX_gMEVZEHszg{pqME#23
z^7OLPd-pl|O64j)b+$dr`Rc*i$hRshMB|&~-|JqmE8(gNo6+)Bh$Eig;d_S!?~9gJ
zi?z2C9$Z+PaA5Kqx%8FFhc37N&f^f3ecb3YOKv`@X%cZp%{ZujfQ)9*~Y=Ae_|>r~6UZRU#|eSJzQtPUDaiq`uSHpekv=ReyR
zDst@hy@ZDn!grn?5D?CEXr5D8-l^_gG=X*Tl3d;mylYRjzM9VFr80F#m`dAoKWSmn
z-^CV95f%Q@f-Is&%$qMV-8k0t$}Lr)oaby4my#y0CBv13u67IlX0AD&+vWu|Em2Qh
z-Oea#QoQB+DV}TF#p~M|RjPIici7fwe3{%aG32+Cv2yo;K#`NjPw+AElqY{`Xa2tU
zleDYQS4)#6ifS{e1GS2ut6%Edf5PNe)p`Zl*3(HG4pS?+Vit9u2rg>9=CpEAE8mTS
zB5W%*8BU&%qaZMgBW-4ef?>v_<%Vx_-FXkccK_teTn^4QEyyu
z=F1Na#;6L8hBXdvuIS#>H1N2{Cz@=rZ0pQLX;B-^FLciPuXrmxYr!#>hepXMUE4M*
zS;_B+xaPe`Yt@D8CpiBdomkG|#KIfFqSRU^@=i&?r+UIC9#+}jnXG#jvd1yZ-Tt(}
z`Y@M5<*B8v-&~)BYP>V>d#see_R+1Bv+_!_$Jg!dT&m4BSG+nW>37e%^MlPMb@{p2
z1#fnH30^LkZdR|o@158$<7AgkM(>m}%-)MOq|AEI*rjlNo47#ZmI;Ro_D%g5$bGN=
zOH)zR!^=x$)s}3muND=ZMCJ3+m}=qj&@
zz@%3G@a|8lSD4oN3o+>(UaRM8;uI`9sd}f_45@UU3FAxre_`
z=1tRMYBOs-6FMQoH~il>mq*So4<56KSmU50ExXxANO(14m$LBYZ+|X2oR?_tOHccC(ZxW&
zg!SI5mG>N8@#@a`WA{Kge?e2tw3JA`!-5>l(h1+(RyH40o4djMv1^9%Ge*_0k{%w`
zpZq~H%!SQu85jZ@_V!hmJ(}0$y8q6+H>y(e79HIc(HKzhSHVU^$?D`9Nv6&PD;4`v
zpFh;k6`FF`;eg`Zc?F8WZ*sqwax1LS5R}@r)M<9^ubpdG3#(bWpVH5r=dp+Vh~2&0
zs*?%?KQVg8KNr#vU8ef7?*6si_UXYNJ=7$gCR}dac;WNC6VJD+GZ($>yU?X|-g4g0
z^UabB-&tt*P%EtebGGx0M)!rWv-M(kCU;TVFhG*MkdKNs9U~D_0;S#Xs(NsG%
zH~ym^Wr~{)6eu}de|FpQMa-hRJg0=NU8s{cZ4hx=&hWEJrfk9*1*xa2j=v9C>K(hy
zfA>`%wkQ`p;V8F=$Yo2ya+fn7(JA)yWQ?97d}R$^>k-9GcES}$E-{98b9^z{+JCl0
ztjlnsA6o*Ogy<^CIaVnQ0R~}gS#2MCTFO51dpbHhRot?4Sb2DxY1msv=UXl9dP{db
znUE2;=3DH{HrH7O3k@cER#eVlF?jK;!_eL+Ric-5h3TUwT(cUC+$L&z$7(q6Wh`${
zoqy?B-KLNmuKdMW0#12HE{0@1Jgb^mz;kN3=7b&F)Iz&9ulr;?C6%jr+O%mOESQs2
zSS?raHu+xj<(D@;#pW8tY1=8>`DNK#MlKtfw+t^MIactf3+BcbEEAbN&&ub~yFP>G
zn${j&FAuP?t$z6Zz4EnC@mCwP#Ko2{CAz+ORe2&{OY0`(t#j|Bzh}KuRjns>>8LVe
zc*JzSaOZ1p-F#EUY+CpHNf2T+v*%WEbqO`SQ>xRZbSMYBVetNvOv&60?qQ7Lz=T&nQTLhR7hh$xHmvvcq;rH^Q
z2}dPVR&tv7#MC67d7Z>&Z0E-3VD-;R`P2o8dmUW&^mll2H-zojRmjB3-j!kO<@{o?
z#|qm?dcw@T&U-h@_DgN}&e3H#ZI;wKJD=kn!tT=MdS#vU(Bco43-YZykDhlB8xPAV#a)=R_>t<;N478d
zHTLh#^m{oqJ2HrQLev^(YtAVTZSv1cv~Jv}68>B0xLn}1ZyqckcTA}JW1uMe*lfz<
zxh~QEfdR4{ZMXSOw}^9oVEL~2VJpkp6mc%;2@hShr*_$0lJg7dR@45^YdL$*edL#C94p|@YI3smrzjOcd
zf}_89y5#sum7QKh)|gpXoQ{gVZMoV)Tl-UAe4mKIk
z@~~~5?c+Z?1+Q*Z-Nbw6*w=H;ya|&ZFq(Xkxux9fcr>%>)Wt`CJ|B0yWB+f?3H$&5
z{ue4l*XT_==Bc8>cw_fovuw>P#Lwi;nxFv2qS<8G)(${Uz<-6pMlub#S_``FL?9z#en_n+KY4PWy$@a*u
z&E3V9!*d@LozavtV?Ng7vEqe?acN({jj6jtSndQ1c?lRPKXH%GA=#BSzH-m%_g}X=_sTarcNV*A!1lC|wGRdE$tUjQ*FL|Y;_d{~
z|G#s2js0vtpEs{#G+7$ZZLmA=1_J~Cg3OSJk_cZPtK|G#y~LFKq*T3%+yVv=u(7Ww
zNKDR7Em25HP0!4;ReHaBzmh^`img((sjq==fpcm`rbks#YH*cbNODznvSo^ry&acL
zg;hmvL2hbEqC!P(PF}H9g{>0UT&uidE0D0hk^)#sNw%$0gl~X?bAC~(f{C7qo`J4w
zMP`|ik{y?VO;JjkRgjAt)QF;#G+U*Nl9B=|ef{$Ca=mh6z5JqdeM3u2OML?)eIp~?
zqLeh<;>x^|#0uTKVr7sK5Hnm-i<65o3raHc^Atd4CMM;Vme?vOaVaP$Kn>3kHSNR}2lsY<=C=RJCNYxKYEzU13N=|hxOU)}$Mz*vdr?eQ^&eGyk
zkPz72IVoxS6}b?bk@dKH`Ub%DfPyhSGq(V&1Ed;RWlAz!T|sG44p>b}vVLk#YHn&?
zNwL16o*{~dN;10+w{a<5fN
zesX4t6_{ygY?5edY-XTqVqsvcYm#D^tea$#VyzbG>`
zuOtzaIc${-!QvIU1y;^Qsfi`|MIrh5Ikrk5rzsfe85)4|l!6T?ceqxRSo!29gEfK`
zr>59}GZZ-6Iwhv-gSj^P_!MK96r7P?o(I+l$+~#VK`4cr;#gEto?n#hU*w;Zm6}|F
z-yCd;;U)#8re_wH6jgc>@D!Rl8%TQf$Sf|&FRDbcKRC4z!h?7#Clef03JTy-z$!5r
z68y!9WvMCPC{Tb&C1)h&rKhIYDnauaOgs}yI!-dMFf%etHPSURGD-wxg(TgiuZG^C^n#S%E}Xz8iPv<
z3UV@2iy*-RPCUV>g%Fc$^l_*|(hp91`2@8iD}(5E&M&Ae%1qBF@h{KAYdsc)5EFbd
zlT-7G@!E~71QIMrm6076G^m1H-0Zk)^uaYEsCI;e1*mqSC59$JT3Vr?Flq@&;X4{!
zqrpW|2#}
z3P-Eg4vtf<0$d^84O_HaS8{Fr6%eq>DKp!dTg>TVkoW4Tx7a@fEsg0Cb#>sF;h?nL
z;gG7hlM1K6Bww@Hd(WM#`8)A!T6$Spxf#n6yZhGlM#lI5);yo{eNMg^11k~0HN0t|
z_l3#{(i`Ty*`7P`+arOi6QbGK6IhrT7q4i$BW2*w!ob(6z;*iTe}=g}HbsKnj?a&>
zaBNw-$H}_ogj^xlTh?o80-@E@w=29;)?r~_VUrYl~5}R*j
zf3}!8eA?ADdXr42aU9vEnY3ri8e8+cxwEu{BK{}T#Dx7T|CrDCN33UxU4BH@*(3fd
zr@KC%lUVXeOj>MtUBt|p`}nKk<(n$~zBhZRE!It@EMI#;ITv5M>Y`(jg6JKa
z)m37<<>cXC+ce#eKCvtpmb_zHy||<6*ZkM-Hl8}YeIEydivPbo|88yzyA>TJrv2%Y
z(YnR+UvaMKUgH0A)+@P7yW$#ord>DxzfXWw!-0|O#E`tS4oyw4Uf)Sh2hJmtR765R($PoA>Y
zrpG8U2>2gv(zH3?JN50Ud7AI{t-t=v$U3S{jv+wi^RL}^?Yd5IBo$jep40Ovo4>4&
z|7CEmze@eSXBmH&tlLteD6q9+_ai0oqtf$vYqTV+tOb6zZBah3a7V|*Pv#Sam;Loy
zZTQW-((8Ziv6~BZEvHY}vN!W!sX(XV$@!7j-(9?~(&+L<-a7VWvw
zWQ#@n9hn%D;%*59Xz3fh`nhh2%35zWQzM3?2b#kukt9h2YsnRleGtHqq>QIeP5BbBfxT+MmMIo4JwmL!RP7h24??WR62lE({-@Fuq(Yv8{A(;jHZTl=N4=
z(%r)A)5Q$C*VWpuXjgU#*~8Lmp;93rx|^li`jp`h7x}N&pgBiBH(xkBCS@WbkB5!$w
zE4%Qh!ghKDO^_zt;LJ`NpR|AJ}Y8j4QYm{b1?d
zZKg&}vA>(-7`!&f-o9vXa8Hi2Q*|;U#{A+-ODL^=IY6J#F^V~Jdu5Po@ZU!
z&)usZGrcTSSa^M!Ricl6>7AN$$M(ov&gNY#jR
zzJ9gIlqKq-p#RAe)%{n+b6?-T657OA@S>Y}ebs^JTy5L0n*!aIoxMKesFG%N*87ih
zcpoh3p112<%9LZTj!BCD{{83p-?oPby{-2J8CW{2>3n9Ja#!fzlgs`|u{Gh@x99JE
zT(a2tP4|i={ilSTXWkH;Txt}5z~Gn3V`F|*E&;=w11CCh)
z*3NlRy!P<1BE`Gg>^48Q-(Gmo+2yZ@Yf^0K4mk#)=gZ&C4Eeryf9lMJ;%9&D%RJvy
z9m)OsD?9#JPtomF47&4{MeRAi^0vr2yPl%Bu7(As@pl`zw;9`Va_HUlW%(KuC*O2>
zF>|nT%MQPqycP5BB<{9d8!DOmZF0kbX{!ydemMQW?ntH6%4x}m&2kQ#?daQn_qKiB
zZgYFR?Amo=@|WsFRV!sS)qP`hx!A68_;CHCD=WWPOJA*4WKhw)7^MF7^tDp1wAbqb
zcXHOJ)_i@d{X6c3fkSV7`5P6fh>$yTUh?BoS@#Ob7}<>xx^gSoMvzE5w7>h5Uljy{PufgipV2DU2gfE
zCTcBP^Y`!ho}R)g^iQx_c#aiA0q4u}D*Gj7n1s7+yZdL$3HFDYi&7s(EdO%T>5}dH
zsTZHg?>zm7!)fIqMZc_H?2Ejv=~#zl`qh*siZaBk+Tv5Q;r6F1;#G5QUEy`8+T}RS
z=wb2}%lP@z%uj4TeWGKwihx3u=aU>yqw`{GO1>Z7cD>19OIxv_8a
z-+zB{_IVxr(JS{w{cJYdfntM328a9ZZ*DTrbba>poX(O1hHLXD>bXW|tiEP)_!MX1
z-ipI4hpc0MtF{V9?5}-kck&J2zS;Yt-r22{(%mDxBtC5Z)6+$!CN^4DqGrc!PnG&d
zOg$%Yx1FJ(+dTJ8bx!Ek#II9b*d=7{KK)VN@a?7@Pc~!k-KVjlyYgo^xIBCnl)3Dv
z{4~)Xfju98%ldxqIQ3yyZf@mDht_9&^^x`N$L}6hI-T^E{mI{1M-Dt;U3s3lq1wHO
z&EdN8ucx=UUp?kBJW*U>_z(_sv%zBqk93UZ`S}HPOa2@y
zt-9WK!P!qY>uhMM!yZmwLM;UKPnt&;9WEVZ@wAB71cNM7*xfSoBphg)J^=zvnZ9nhv>A
z%SDgmwy&L~ozZFcIeeG@s%`ho3m>0Z)TVi9g;k|&_7tvf29}@a{GM{nRp`$r7MB&V
zA6_2*y^K3|^&9J#;!&r)dtb0Rs9Rf_*NgIHzx^#7=BC$D6S}^Vk!$+CD6In%{A@R$
zS()(mZhH6j4CzlH>5nfb39ObfRLQ))(fY60llF;|pUygV;BwVd!Gnv|x=){xLM|-ofy|#8gem##d(l+VeleXuZ^w#pE
z1aF)AxN>dm7mItlj9&ERZ4BG~zt+gEZ0(#g0tO6O=Odp9`(AtUW4TlC@B0%YCDYwQ
z{Oj`HR4{DVI_*vQKD9j+hR<_evRW{v=q0M^9o~CXC!;j{_@mwV4^x&edpGIo@%$YQ
zo3dnHr{0|=S|j48&K|x~pY`U&%I`OC|KIm_dn0@Noa@Ku-cO2NzI6MFuuebYU-JSt
z@BDXQ$I;+x*N+;#I2MuM%DZaOj{7d&=WIFi>%6m70<234<|#g4_B@{b+WKSdy#IVx
zM4p8{y=U3~`SgNY#qtjr-Hg9hUU<7cHLqqrnIOmAWH_tY`@MXcgz&EYv+Tdk
zK7RDK{mwFhj>?|BzWY}kdSY|(+K1j>7HJ#KsYIXS@0%AqTeNeh&8l^8uNa>CB73*F
z?f1+E=cw|N2ftsru98;i-TSEf`$li22^UUVt+=?$>{xo)k5lLG)rMHF2aAGx{LPYwOzbAh+${k%sX@8rH{
zw)2Wt;OO1DKji*mpP(qmj+qs+=FdL1`n-A8oKxM!|CkcD>b_iQ|G8B5S=cW`P+$
z_f506KOo(CzUoxL`k+q5I<31AmrdNSJlGpodwJvK{OhXzTGCruO?UX&pKlBXFk0ipT``*G2`aSN{f59w=dGZ=3K0|`h)B~
zCWbfJas{PJ9b)1dzeFze4pKa3bZe{m_4@Ct6L&04TlKo*%o_v2+8ZLOZ!>m-_2W{@Cw1GYQj;{U
zcd6epYuz@hY3a+4`e9}24D5o&hvqdWG2=q^gofy0}hVt@#DoDwFO7Y
z)~9KnZ)Qw5^2G0G;KQ|^FRbo&I2_Wwt0=(c(0%HXEaS&%j~BQ<5HGdfQ`hwMmkQgO
za2LUf1;Gn*6GG0nXa+3%tg~ACVkQ3xhX6r#1EDI8gab}3m)QF*D?U)HP*`v`pKF6d
zyQJgMh1)`Jed(GQoAq?ceB;EMowKzjJUDFeX6<38BesgS?6|a#waXk-UvaE>hv>mO
zQojUiBz`9B^{fzocX+$>o3p~q7iZk%YT@AHvFNy8Rd~mRSMj=OhyFn(#j~QK9^yL-
zf5{i@OPuCbP0l+XkKWhNx~
delta 3684
zcmZ3LyH<9Bay_GRrn7T^r?ay{K~a8MW=<*tgT}CfJZnjWPm)JaSN?<9FAkRou-rc&9#HUY|HXp`b?z1Xu;+XJ=l|Zu
zX>(gLZ{BM)S7Db9
z-`abcli4Qc*qIrV0xnKYdnNODgLN;FcS$O&61TAmmte*T;?m8XkiNX4zUb1G+q
zeyu!i|9)rrGb278Yl{v~_M6dxWo`zG`jvuwnRX;3tyx*fyYR~Ll^Y*<%@506ZJIRC
zY(b}su*igqS3Kk#&K$NdHt1?t62Z7|K~A3mx6$pr&)>=yw_Cp6TdsFl;O+a<_QiGY
z^R3_it3Lmpi46kkArvlBmeWw@gxoKY!0^LUamRw+G^?7{mBJbkImcS)rF>d5mGqMX+{{jJNM{cHb(WcW=w8;-YOCtB!7Uj?k0rnGh_ngsFb%q~F)8
zzOCG7)qVB#3n8^F8&VvC3W6@pb)F=7a3{~Qjk2v02>h#9o
zs2`V0{-ZGW*VZ}=0o~F&XLsL9yJl*mxxc>ri_LWTLhHN!WjD_HN4C7Zai&t|^O~J=
z)R`u=MDM(Ob;IQMORnDv+ZD4|*(m+5*UzTsng^c!@-b03WFt0de$6ZY`eOy_?GD$<
zoITLolg=0}R6LRY_4-JS<0cNJle?CD|J=`a%iV})OUF_l&WN{;Vru_)^)5g8`J(qs
z$GOh$x78b+a=owI{Aby!*E`N!eJxnIW9#*_)n3apcWj*g?AerCkNu=t-MrM!aBZ6K
zc9Zcj1KmEJsZ2uO1n;W7e=$+7bG6Bh+KlepJr4ss)lUhk?bJ3eTA97$2NT3RYfPIN;lChfv2SEKfXGB
z%O;w+cd2={gsf3&q10)qW72cPz1|-7{*o4$7CG%`^BzehzJ_+mqpuYoG3=NUEt69{
zv5LPo>_A6Mx!CEx$@S;!ufMF?opWygQj=Z2e|Fh;J$-NL>zn7=Q@!SG=lmPaBCL)A
z0VZkwSH+j;EU&06V5u?;k$Gv*^hxAVM&uL8b>H3gY}oQWuK)VywvS%xe0JV&&dv5P
zJ$;$$i1qDF*B-8R&2@O=R9*Pxtngv&qtctSj~QM~&VM9!b9HTT*e(AXw#@ZwJwi%y
zYHD-t<^S2UWA@9)uEh;PRcROU1#7LI$xNEv_HS!gbC`%&^@{p+hi!B(AC|dRvNbzO
zVx{2kqPrRQ@r*pf!&7#)kessJuL#u90
zmuuW7H;GghX2EcVt1I>@{Q4W}_j~tN-D;c8y=PKIrW?-x9<=9ot6h0ri?xIo<58Pu
zXB-=6TWS|`Yd%ccWi(^C(~I9bdjEY%Qwz1b6~ANNv?V1`alZ>xPVE<7RmfWWn<@76
z=BI!E)q5vQjwsHJcMO!B=vFsp$0A=DL7gcwzl0WxAJyy2o}#XNSoW9I`R{)6g&S?O
z(G5_l3DgB=#
z4+-`K9g2~^n!Cw6v&gpp@#}?58w*`-#Cfdke|YhT?56si@8_KAxI25cNo(yCk&oqm
z&v+KhxX9zUVwritoxKIyw>^n(4|?+1#r%YN-}B`eJS7o-cicLBQu@h^!wc6XHFwt)
zrZMg8UD~%?y!K0J%tc=z?<_m0crWCANpCBuxWSMYJp6|8PM=bb9Az5C_&
zJ7<3O@Mo||t#t1&6e}-FbXPLpt8+58@?(|Q#mz@2|4^PNtbBWFs^@9x+WKi_()Tnb
z6&K}jU$b`Q@mW@%a7sFT>E|_p-ix-~b@dZWSQsI^b^7!v?tC94-*23_m?J&-#kPs3
zcRyj+F`Mzmq{Par(MKX5?VHuK>CoJVFC*m|QvxphRO*miSE$}JQ#H5W+lI4zwSr#Z
zZl^-d`Yx}vM>el~&^P%-#{R!sT0Tm()Gu4I=v&d=cD~KkD*l(^-kdLZ`S0z$obzQ7
z9^dYp)$IA9ns?04Th8csOKQYPSraz)^CiFco!Ykl-QAW;+B-AsDtUaeCat!SoA`N>
zt8o2B+sUP>o4E=ktUgO!P^^2+@w9-o$>?6`iJV18zISo1cKm#MIXBlHCCj~gm;|{P
zQUw;&M;tKx#(G+3V%%Y;tiuz_ikEFO=4*2-VE7Ey$?@pvHBXN>ChncYQO=plieBax?Jf7&@qM%3_o*!B-<;oVb429Wj;80Yg(jRm`e+_=
zY1_{uI@WQSs&nKvX&crbND_G-c%q<+$?WXAjqlx~KOH^0FLM2fg0H6Yp66%2KeIDu
z>+Gd(1zNwKnyD~b&a2(_Np5%GqmpC==gBu;I+pvo
zuf2a%cmazD|NfQCvH2k`eh(hqGYZ!GzfS0gH1CeI+dsc=5^K+j^-Vb1v%UT(mxsks
zzxvOn^Del)TO4G2h>vg50|nENkBYnR{HnCsSQ2)6&(wXBL-o#Ex?GqZs`&Ft*}oT=
zJ3C$X@rza7IoGpBXG!x%w}x`RX%Z?1VNaAyz`~0FBG4)xu4*Prjm-$;!7utR3cHCfi9&jNLGwN6K7>27jg7FAqUdb0n?)@}TI
zep~to^Il;WKW^vs=d@0#Ov?V(-{-Hif0>p2b=9Pga+etX-MqfHW2eo`*47Mm4(D6X
zs`Lu#4{2RvT_54F=v-}P+&0xZv&j7uUwqo;*JY#f_1@1pa@N~!RqfGTA1c-OpYKfk
z(rf?nnm>QpIBokTzWK#JY!=Vse(_YeqeUh0(U0j}7gtvOSlRR@G1uX`s2}^2nCcxx
zXH1=&@0tE|@!p}ou3K-5TI6e$ko0w%9`i?bt4w@$Vr@{p?9RMr)%OFR|N3*`v#7fA
zQDK&!GhQ1>-JZ<8WU}4EWkOFLosku5kjvf77+AS4FIhUbNA9OXdPMoj#P+#6#OBTQ
zIJ5oTX1^tuGP2wK-aiZ${y9Ou!fwYS%TKLFvDb{JGD~iFDbzdX<^sMCv)0e4Kk!-f
z?B62OT$yQmtNxpmcKxlF(#<>f(=vHYYNBJ^wp;&~O9VQ#EUjEXx>>wi3|e0*(gk^J-Le}3FH4Xvg3{cERrFleJ}v)D}v4UR!m$
zASNc!`*-!e_Em4VzY0`_?0g?(;C7F{e&eT$n{MV6>YjdFysYKbo!ysi+?}QV{I&AY
z+s!YuQ@81z@8ww(z-8hn`ujECmq)XXgO*mwx&$YZb))I?~GN{nhif-!^LBJ$r}O
za`v@p>h)%8E7jW^+c}@;@*Y0>(tUcFwsico{2ez6ADy_U?{wrNyXk+%bCLoljtCT9
zd6jT@u5lsXOraaMj`-Y6Z7ERGS)y{ls_(_qHsf?Jh53~&7guzbHazq`@N!?Vlht;^
xMyFlL#Z&w>QhR00&X^lu0a6AAFGT*xH+{P;WFfxoF9QPugQu&X%Q~loCIGmCL`eVu
diff --git a/lnbits/static/images/mynodel.png b/lnbits/static/images/mynodel.png
index b8afb9ffcd0dbe9c3c07cbd1e007b1067d92caa9..344b54b6db53406258f7f1207b78f72b9fb1cbf3 100644
GIT binary patch
delta 5190
zcmbPGu)|}5ay{Grs*s41pu}>8f};Gi%$!t(lFEWqh1817GzNx>TW`aow<()*{JE~R
ziKVf{;k4uW;!Vvu_O83%R@bIaGB!TxZuiLUUHck02hr9!-{kkt{~~SYb7*B()a$jW
zHCgND%9>A;pI$-<#AwRyY*w=n-*URXP?0eeRt54YN+p1xn
zxkU8&smQIRcch=MI`N##{rr=ECoU>4T;KTobFtQ8^?R*e$7XWd@6q}A^wEy8IXl0I
z2zwuNKALiT`nn_O4xHi-GWXYuS2Z=I)SWx)-uLg(XXV7ZCOb1eIK55%&27J>X1?63
z?>(O+{u^A0yKWva|FKH^vD4X=b)Qen3;yx(aB+R^%{
zl|?&`@kEr&Ynb0)-sjRT8WVBUp_y}?2+!x!;=8WByJPm^}oqiCHvnS~*3!UUZ>b#g2b)>-ire^+05;nu6a!0V7_{EEbgubE9wekumcNim!q*$NC!#R=1Tvz|KSEyxo3
z#ka~yBF?K}vxWe3Q>pOjh2mxMCu+sFhrFF$@Ob@+r48@y*E{I3mc&o^Ew=srf^CZ=
z&-_{87LvGY(k$*%{KDHTXS+-PeVP61s`&rpwh43XmiEQIIsn%?BA?J
zb-CAcdb}tq<~dm`XmRO9M3-g(~UJI0jrCe?#O(-p%TqrA6C3e@A&4$e&Qk9c3pchYmGYl{AFJoKi$))
zU}rUUHd_#(%lV{;&EnQmhcY&sQ-{U2wP(HCm+REHfboJL$8s6wuqy|)-s)NyTq>l!
zFW4fY#43b$cgw|#f7^tXJasU8`e}brZm#i{lWrRm3*HMJYSU!kiPH6EjpeGB(a8TX
zi@*NH)#r+j8_#kt>hG91GgvI+ROLB_HG0i)qU@RJ0c_d%Urhh#D%jc`SlSeO`lwrr
z)aEP6{I9#%zZAV`ThtR^(Xp;VVEJMZo~NrV7iKPaA;7%N^TRK#whXfj!GtXCqgUTe
zRGRW}kL8ZO{VN`izS%>|DhU6P5h~IyfJ0?G!t5
z@U2(%1EGSY3riROD(={0d^U+^_WU(BuNg0R6!`sO^M#kp(`WM9M}M$u*{f!vnzvx_
zoTJ}f{=KEHds@0}vCvxgcWjHKxNh|&)~2@xOxd|uca2E>^a)k2Az7~%eB~`FF>6#)
zSh^zN;60mvI<5uv6SJCT-pXhR%1!;s+^&$ra%k($2cK45yAa}fU~5n7p=3949m9zR
zR~oEJwC7JaH7%FV&Ze0^I!JsEL+XUB?58I9#Ro>_F+FIR5UY~-R#sO0+~T8(?E%Z=
zA{{(G&zbI{qPej1nQO9opYLbO8&>hbT&6yeLKhFar!0{Tv7NC&s{RhU*FxUz<8eik
zrb$_CnCSMht=9FxAuXo8`^8)~352pAo}l0HTX}o_fi0V>xWx3g2>;prEpXmR=Xn9L
zbzA4l?^gSFQv9F!7w7OTk_*15+xg1rn?1f#$H2g-lIiRm;OXoPtJfG9D(1|cVC!`x
zK%{klPjh|7(ufWZhx*%$(#grjS}cx=#{^~88hNs@PnMX((`;R!zH5di%R$v046NDb
zyT!zoeLVQICgz>(^S$i4)|{6&?Pbmk6J2-zZTz?OZ+4oC-B@x~tlBZ+S?qc1g&%A7
zB}vx4lvw|4r~UaHk?9LdR-HPuXT^bA;?|S3%-1A+-S%9*Oa4gNl(dOI>OJ?!E}fXT
z`StRX7JojPY>({P++BP*JoiD-8BIAe=3_k`D_(dQm-ZFhn7T`Z|3vK090vdh=`Ue(;^nyCu?6ZiNWl3i)zEBCB^|8=`_uY9v}XR*5mY)=bW`%vJX
zeBw@i?eiNd?oKfM|2vo0*w6O!dGk76Mw6uh-3Ge@Z!jAT4q7AK4;p-^64E|6nC91qB6nPu~Emkj&iF
z;F84L0$U|RGcyxYLn8}wLrZgGV*}&OGvsQRCc7xasF;`;ni!>+8R(j)CK~ISSQwb-
zCK@H1>!uhQm|LcrSz20}n@&EWpuq+)eey>IQ86O}BO_e{OI;(&5F=wN6GJOQLtO*Y
z&76vfoI)4V!)zEB7?_g0-CY>|xA&jfKe$!D3T^vIy
zZoQqAIYs2E=<)yCSAExFlVs=VKGLevpe_v2NYc-BrszK2dpp
zJ$_f*-mmw*zuWzNFB1z>ql1C~2TMJa#!elqXHKc*U{es?_blhy7d@E?8VTM;C5P9_
zrIbe79-QFa%I?LkyEiHJd)D2+=7rL6m8o3cC$}Xje07v~P@im_Z)CPisNHXap3#Rz
z4rZ)Oy!MXIZ-{)d&h0Droyc0UQ}_Sd`d>~8o7fI>Up_1O>k?C_*27%B)I)pR>VsBJ
zjcc3Z&lSJWboW--wY+<>v}^nAuIr|+*=;gU?ZJ{g4IB)Px8y6@#8t1)d=s==XM4a_
zDUV1L6^sk)nO!NwJuCw9hI>h8On%Vl+|V`s{w!;@c{TO2B7xcjnbm(H~_
zUqAB8wl%*9S#gi;X@l3rb^AJwZ}MHa#aRC8jo;5b%U?UMOuZUB>%~{ET_+dvDcpEz
zI?piQLz>~3Mb!o+sh2TRHy%Fhe>sW2RA$N_iOj{nF8h6}-zgpIpxqRGWtT$esVh^r
z6o<%j?&hDiuF#XOe4FweiF)Y{IX7q7^=&6UTL&+`6kgYP?1#|nqOai#&vlnDtmV;v
z`(}w|w4#Mo!1JgAu2ot)*z2>;G&ShI{crOn=h&r*86C@}Y8Kzl;r+JJrU(7pXNWKV|LWJGn%0Dh0+(l^r|VO8vA9p)xnQ}*>POkduO=*Et!({p=ZC#j(cJJY
zeY+B}cOAUXvT5fU>&^3*KhAIxX87Q=XIe(LZ1=yuqQ_Hr^M5<)X!J4g`2PNnY;r+u
z9;ap8Htx6K{9av_9Ur#3=Oj}{-bRZcqq{NNLb*J=7wxoAtXX>0WN-eH+7%a7cNzNI
z)-NszzW6Ssobgxdtt-bD%TBEQ!<%b(V0R_kn~%N|>YkN5eEfQhBd6fYhhGa?^d4@u
z=neBbbFxgwD`&%>NwZH*eE+6^VPV{e@eHTz~D_kocz;_5Q_+
z_D){K;2^o=nrk)F$+ESI)i(AS($9CScz@Pn%2sDfudaJ*urHmFkQ?6r&KHLK-M
z8ya-}-E;5S?#2mP>6^N^AFYbePo0pyBq98d%$}EG7Z>HfOI*Bhg+piVEtbzBdEdT$
zDSq~u{}jXdzpo8;$i11J&FIkeQRWDzPeB@M&HHV+)CH~x}LqJc6?g8?w^1?S54<`
zeSX>b=gB))RS=Y;XRZ(n{lEs0w_Pdczi#F#-t+-vPV{*$({Rlm0#dbTjw
zqrPeW#>&L7x7!yjZMpeNH|~4Q&SfVgl1sAuro_*he&DHFo8PC5&9cj`^9ik3=9a~|
zZKd~=eGCii&vMIJZvDjKQ6@b7W
zyrw+mN7}x1Pxeh(Uq0WvJ7*sg!zTk4#*m9CnYnA&>n9j)-ki{X=Ju&63vAVH&;Dp-
zW0<((&!*O7?Ziz#V(X4PzRLDDQvTkxw0TcIYPs#_$O(Nt_dCa_&XUQwm+Ve;|2d^T
z*Y)6E`Ss6K_ni47!cY*V_fzAg34=#!<>s|bYoBH)Uz6T8_0QD4o$#rXRO|uDx;RNmkK~nD}XpJ8u=f-+0aV^M0ZKYYr@v``=Rke$t<5^S?4O
zXx^RElN4Vre|&m)%)zbC^P2<
zE5pWp7CSFU8=sC)JY{HdH}T1Q*XbV~rq294=bp%UZ|Qyg5AQv`-IaFZwER;a2i~)v
z=SV#M+tTZ>?dYOi`ChM^cJDG>oy?%H^7#F0a@XEnk$OD;N!jYAt0z_m?Wkrmx9=RSlt#ZE2(JJQ?
zJhoqu1&?9%`K
zqwbkzNZ*>9g;S;Yyn2u3FS^`Tv-FQupVrO$wy$`i(gbhhCA)87JN{WdxXnuBYTo<<
zj2Xu1Vk@^q{qkN}nI1P?EA0gP<%IHjQQuVeSI3%fOzW?=H@eBC^<*w{yOhI+s@rU@
z)n1)H>)kDPLdGPz|3mV$<8cS)78yI;kn4r|cV+59D`
z0yg(&+`9U9k5B88(tWZAj!%u1zp;8dlS7I~@8@Wh!_P$Qr+ajkJ+6ssxRm!MZ(l(6
zn?n+1MRU!RGeg`IQs;bIHhaUo_kO8byX^i=JIQcy#TJItno~0~%wK<4THhZ!>Ef%i
z#Tyo`D4Q;!o;8>EnZ)}~CX2(WHx&tKe%TaN##b-MTK
z(kriiPIZ?n{N((7e*EiNUc3G4-(UatZ~uqiuh*Sf|G#6;lppi#>i^4peEs$C>pwF4
z{@L#16WMqFf9-%oCzy5yxT6*Bdf13-}xq^Poxq16K`#!!YkFIxp
zdOtnPJ%4}ftheippRb?(>->Jptr6W`C$G!YW|w{_?zc04l^ONoZedN<7tI&_;`{#p
z{d?`itVf=TmHUo~*su2aC4K2h^|ShHkCR8=PY=qf(OtRcP<7Nvp@^41tS*)ZYdl-O
z^=IVb`cJR-7e&S!R2uxaxK;l6KW2H|zos^~{#3dD+Vk(T!`I{A@8A0Mw@Raa&%C-h
zcK^05`uBI*kDvYTueFV!M`@ioo-)&R;bk^(ZuC?)xes`Mdg}$y`^J(|T
z?H`KktM{|--#x#MS+=;&IIz3EN8sg0UdxCX58FQPd%qzvsZZT(-|-!p8{=MoNjl>D
zo0;Lkc5d@+CX<%(@t8$;?vbfqqq^sFU->Rss~y`9SSLS7aNP0h-`l#s$NxY1ZZGp9
z?yL`oR+{(vu$o>clhXmuzf3K^IH6#FmY4fo?ZO{{FXHR#S8nX&n->1;9IM}cyY>jx
zKS#oX=9~U)y??WQnYGDpD+bj^cUq6OxUkpou56L2>E77tAZz`1<{F!2GT{Z*EkTPm
zo|+N7%A{TM!JV3At>5oOJbIpbW3t-%>7s=og3&7Jx?TR&Xg`a^1}O#H3>bsvke@3(U-nq0qV-o=eG
zm}T={F*NkOn)zPw`HFM8%Z@Fy_Hpf8vuTB;*cC&o8U7tl+vNBDzPWS%o7t!T=6}`w
zchhqDwZHi#=T?^1m%Nkz{Y^a9xbxt*`I#wPcePjf_sHkZ+W47$=HK`A<@Mj2|Fg5-
z;{4#LkiTB{d}+e9&^=pc_wG8s?~qmEnc#Y-jbe^wQM2?Xe0uBUn(H6&@L8$z((5&8
zYeTNV0>#n)veVLB5t=rdUPF?@~NK$}kzE^r%my>9H%%z>k63+=p^o1;Y2Ih%Hy0j9R?jxHyj!;Ffk3m9XVk?@nl1%vcyDFT
zWu4FVBclAfyR#ps`niYa^d{&>T+4g;dqJ%Bdr7x5mW3M(^0zhrIQzvf(h
zbn3F#w%w-dvzJ?(dC+j^LvWeDwx9O`Tlr__uBU3s%1AG-XS?n;Ir6rQdQ9>U$(2Sz
z%llOot{5I--e#O@ovQ!pfo1R2cazU
zm1P>9xmNbrVsF-*oi7fuv>D6pH9sBQy11}S_J(wLz-P```>P6IoIJFzv
zLfegAoYX6po)UICr((|3{KI^$Re4S?A0EG^DN-wad#>{9D`IW?*Xowszt#S;_#88kY9()}uz@
z84faC@!G`#Ir9oLd-TJ%rQgrJwtwvv)cmKFiP^}mL^2veod|NVC
z*P1b&idtjwP+DWs)`J%ooi_|qQ(M%btgS8k!%{@!RN~#kO38{n8j9TOmKJZVnAbER
zPos|MuS@TRU^N3BHM`y$(~h$^s)lWO_I6$GEy-o|KK$W{ex@AKVM(W~pDp;cb!CFS
z=vUU?t*>t^*|lXtbPdyLUzud9sLoavjx8drO`2)Xo-8+hQlPPe_ohpmY|DqeqHo+2
zbh0Nk&Qt1$@hfVORA;XD`>{$`CQttB8E4i%7H1Ci%FM98DsjK;_Qt#Z`>f8SEfTZ6
zp)A+Vc-VvYLw(o%RW6-{w&Cju
zO9GCSCSG>eTdpiVBES
zBem&oj$ZsJ6+DaUR@={r!|&>yL}s1;oZ(ZwbZ(5?)2tGKQ+^8$FwRllyWVKxU$e9|
z3p%8%vkqWY3KpU!X&HR5t^{?Ta~?y_OJ*)-k|hpqs<
z_lvtM7v?;^QX4qI#!l_(FY5^C*Zg1Sm;OO>hil=dLM#q%}U0bT>*2@+|
zE3~bgFKVEAO!gV$g*>OV9!Fv))!2C!+CF37w(S1gO!FhNUnxY0Da4k}Xqy{ss3@X;
zGP^gaZ?!G=_40t~drQ1#dH7`}d4FJc;=S#Ef_HAJP4N?t4HnEdl0I{ua@|sxbN<9a
z%`!G6;r#b)(oB~w$cl(R>~(o6y(ND0#sl?&MrkacJFjgD@MwCslR-e-EwpZfen#iM
z=|*N&K>@c<7z73}bFnPcb+|W?x8&Y^*;%vYvw!+XEjY65VO!t~ZpO!Z8YD$p?To!T
zpT+reUsF?X(qVt4%^1Y_LC`h&ah6DPnUPhbRChL);juLb_p^7K9I)E#H&y?a;hsaL
z*IAy|_xzT5RPtcWsjL4UmTfit@J4TS>PkIyG7H{BWT$UTUl$$+Cax%-w-?SoC*PN^
zHgir}{&3;KCN;h!we9hWD^VqN2jlvE7VW2#cCI+AaPXGyV%Zp-
z$O99ObUiGtS$emas
zg^3NKJ`3hsDJ-A#;XwVw7~QnGw-UYw6g(HS*Bo9Wdb}a&i`bmrwq++~?X5{{J8mV^
zSLJgl=Si-(mB8iXLl*N6zGd0*;^3}$LyI>-{#J&U-%PELW;$Br$R6C*ea=6B#ztq!
zqhC&LJDHTD`R(GoH@syZ+$(IIWESpuRQ@qqFzA(_&zi=G=Kq$P<~x}N)LXsKm{Y`k
z`IuzH(_KdX7agrc6<@abIC3p4H{qVAa57iFAMoIBGXAkkrNvPIgW_k&E@!W}(!lF6rbs~>T@oi>^Flt1u`kjyzk_
ztghLlW62s)GkM#IvS1_kj~@hsxNR)m_@A@Rx|Ut9dTpNPp?{CF7W%x=n8@RnD!0JQ
zM`07c&5Yz9lKCgLsxJ`=ilFV>txN@IBs@rhSGq+5Uxz((;X=$)Chdb~{ZMe3^KXak}jZt1UM_33=(9
zoIm?7(%{s4;w$y)2wSe&i)}%Nd7WYB^j12N?X+xlF#vKg{pEeZVnza_n>bB`BiebX`vtHoQK9oe2g
zS-G+Fl`9YIo)ot1&%QXfg$CM-u70R`%j0~REq(gUbGKG2#>C&9u$ZIkqs1Yv
z{gYUpMt}S(81X=+!9&lhWQ{=m@`p-m*m$h9`OdJ#EZUg;`t%E)i$e0v@A?Z^)ujvP
zw%9a#)O%_NaL?J_F5Iy9{4EDwhB<-^yQNM$xo??ky8WK?OWWy7L<*kG1ss=i|B``E=#bolSa1TK-+SiUXdf~I?!cKwa+
z&z+$z&FQJrLS71~nyuP3Bj|1U3GpwCzZbmwS)wKH$-z6<-rQnZzm(A_>1z$|CSMX|
zlfKMR#F@;)rc>)C?CG+w{EIL;;C%5?UJFaK8$&~uUz2t@XRh9=_`(@gd
zn=b5>{<(Zl!%2zLKMFakrT@*`!FM`G|NN)=q;222Zk^TAdOZzt-_HI9gX_Xhk4q5IBY5F#8Re}_+{{98&Z{;Nl^oz$8
zUu$JAyr`d9xwdPKu*c#LpEXV}Xf>6t`)O0z-f-c;^RO42lO3-qXZB{C`x7|Lw8LYc
z;=S^TL5n8tIyLRg!bm7D(tS?hpn!fZy@e2hr)vA3?
z>Fz@PUfIe@&i|r!SL*H6X4$|YnHOzt)!wJEnJH;z`W-HoHq|`E1E$}VbiJR6N6G6~
z9AG&5u=FM4p1aRn62Gv1pQV^4!_+Rn>D(=WcI%EcXZq$#UW#jwYHzWvf^aN&snAhE%!9dzGSwiqP4N0Md1snN5>>$dEWiNvXH~O@(MH-Y1;~1b+QW3IL8#JctZJr`|kZ09#5T?
z{BVBa^PK9eU^T9kBkMoz6xlHC@z?qF9a74L#(Jq*48AM6RUhw(I?A@KTfaPZH`fgN
zU9F9iI1g!=E7{dvzAJaoUisOQVy^rOzD*}C<;~^4#r|1C%y6BhrN@frB>`>Sh9(*E
z>$(^&tQ6T9Zhe=z+qC%lV~O|20*~BLx;Ufz@4Po0$M2f2oxY*t>8llD=j43Wy-{>O
z#9qHJim^#5eZ?}lK<13O$$e}0ZZ4f{_G$TV72`Yav@#s8u|@8eQ2!Aa(wnzbK=Mq_
z8)x0wRfkhl6aMHfBXfI;xvGo0}c}i?H<8zhfJqrc((vlt2EFQVvsk+?d8L}jQ
zOO5u8)Be|&S=0wk&y$^V>$J<#EQYiXu8xa3r|59_sHUf6@h}Q6_{i{O8L#F8Z;eC7
z*P2c|W;MvmU=BO+`I%>m&yF8$dS}j?6_jYK-?2p_@y^TiqY1ulNo7Amx7E&k!8YMA
z^M%lZch|NBPD_%VJ3Yt2`)<@Fr8#PkCQ0>$cs6>-F4+|zGVNwUeSD3?ue3|9{#Q%6
zHwWkLWtfrKba~l_G^dTdo-MCR`Lo{(7p%SX{+d=PgXFFdpXJ}YBV;rGHsqaRKdP%U
z^|5gJ85z$Hn<_XBj~1lbFP%7hnd#A;8b7OUbIw{ZD|KFZvxI2widU*m5
zyK!l=40FF-)%CNAT&d3GlF#c+ZrPL`Z(Y;L_3EEU!f|fn_a`+)Q)9&THJ0h$J>Je_
za8uxKz}^n&6}t1f96Bzuzdura?Co2DS=Zad8H;zcZFZ7YOJjUrIMF=HiD%L?0ry^y
zhB(#>J+0rxHnyx#NOF82<|(L&l?rDygsU>z*3TTU;J&ZNNz@x
z`mdwuw=Uij+NpA0@it$bYMZwETk9{|w_f$A=au|>ed#Tq4T)bZnAdc5n~42p<&dg_k1J=biBKEIzsed+9Z>CHx4Yj3akdQtD$Z5bA^
zj5kt;O5W~Te&|)Hg2C2Q4Ti(9+&d4>xncchT4A=7o6*kmZKn^;eX=%oTGUlbrG&$M
zeS#Z~i!k{r96bMSWWA#aU>l8tTdDh8(8J<^Jrd?$ClJ2!EHb^Ftp=e92ygX1c9
zFX$+qu$t%T(r-1jSM-dvgZZQ{W_EMPH^zkt=SE#pdnug!=+T9Dw&CeFY~Bj5o%QvL
z`;}EYvm$peAN2QMcGl?ogZg#LzZ)pbJ6004FZ5WrNSNAkhY9nJ-8j{361H)cVNl)O
z#|deRqmBNi5wY~ND8bv3(mue74{9QF0il@hg|$}YFLD9gLu+`;+s(dtc&Yo6&|*p_$TcP(dHjQA0^hGWbxGaUT`6j&`6UyrmF6npAkx%udK5pf~eaIR46
zV^!-qGj}d(>}ys1CVSbL@N?u;Gq-0q*se-c_#K^vtD4)ZzDq$;nHYIT-kkm$j|4
z@Uc?r%F0zqKa{?BTNMMVUCsit{M=L9*yfy&|M4MeYh>HU74xef-I&{LYgx{={!Qs5
zH!I)2($T?lwpmYH;jy;l^n`=AyB;5yrl}w;td@DlXp-(WE-}}9L#O-|Gh-~W4$n1{
z6~7y9USA?O%a8Hhm2)zIukI$TvVGyn_B!}yzl5iw@t?*v=Tj<6zwZc2_J~R6c(A2U
z>2YAmY7xKwh?pNP=M$ozd~)K9{kSOn*xRiAT``~J%DCJgHcu>G5o@)}ou6ZQ&6^jI
z*TbeVTb=w{VJop{_5>b*h4cO~O8aQZly3d6_~+i+OKW@UE1M@|y=pzVAm)Tf^8~&>
z7ew;E+$ne~M?|F6Q2QB}ca+X*)={K#-4OE+;m$hD8x|@C0`-g05SA>K2
z8!TRVY_9S0`65kakXfeeCYuIoC>-
zcg*iNVG?Q0{%&FGo2fQ&3sP8SGQ2Rp9C>7cbWL*l4{=RejQ}eBJp2jgSFe*>}z$RM1-|KLI$g%pBN9%8#e5Am~7|X4f
zR+MBnYRp}|
z$v|Ig!?&a7*3_TkUH#%7-yi3X%{Tfk=?K|(mM=0f$|?J6GPBKo-ZQi8!m`4=U#oOu
z`YewgPCGJ%lT*|-bJ;mTzUrXqO%2svPMJ?)eytnlB3S(_>%M=&?QX#O*boZI<>t
zF`PKvbBFTOKJ5U+A-i{gqxOIv1@1vZ5ISYqqXkTdMQ)
zXH4!|QTJU@Ti0Ieip_PrKRL_Wm8JCO0i_3*9b7~v{*nt}sqvc0E7_p(NOdY>3zdMh-fW4mt|cbQHt{yu}2-Dxr#>%%5AN!@Wtp^2mexW`DnO_Wz8u)+nEN
z<)fSS-YHimyx;wCR$PMbw0#1==uUgub5%C|t*mS=yWRC6g
z-#a(nnE(3b8Fe~F&tlFvD>@!3z4K)E#@>2w{jI%kf2B%JvV6VV+kKy}DBt^T4lah^6cAB66FE&8d)We%jl_|zrM%+--#GomBgx9f1cO-)ji7m?ZDpnB~ejT
zar51?cb;s2T{-1j=2cp&cU-vs+HUXmjn^}lxBqipJawff!MPo?tgmN=Tnso9*@an|5k
zpQ`Hq>Rj@fo(Zd(&ph*Y?B%OBkNmroSvbP&`Gsr0Esr%n`6OcOeA~OCv0G}33*)oT
zGuH3z@44d3R&33-;D7Ps=^qczVP9>hx9j_%+SR5!eGi57CkDQp^0KIq>-X79+9?e}
z)_Ofb%U^%`SA1*!(z?zzz6*1w_H;>Xvgl3YeU$pkb=$WGlDmxcH*1`Jm^aP+0AqcD
z{^3ePCcXyl9oG(TTfWlp*78=HiLZCp_3Iwre_w0zQ?|PmPi{?`^H6BY)3dAWr+lZ2A19yR-1d)Nq(8_-aqZ6bJ683_3Hu`vNry7
zd;L6hLScH*l!H+V{Zg5gRiDMEyq;^_ZCz#EC%pIONng=peyHlbQEM-2L6G
zcdwOM|CqeCseiMb=gf+1|99WIub(`9W&exf&(T3IJH9O4zrNJg+V<&*=6Nsn9hkW~
zyZhYy2QuYNwL7h*{+}^xdd0CX28YDT79QFAy=ac*$9&(fukM%nUVL2mt0R)0`)WksRR`kD)FYUEkmAq5e=;qG*0u$HrT)Y3;Z0$AkuYBu1Y%TrxSxIU_
za?RwGQ;J&N-Z|DaLC#z97}K}l`g2BUtWkfpc27v&_j=#l1@|^q>Cyp;o`REcn>&9B0d3|3<&M-?0}&{+xFB#bR`Pb;TVZ47Cskrq>*n!oS^tQOfAut0rInX^R{XBKJgwr-#CrAm
zU%WP78!Vy(lO`RpbpH2X&x&%-?;$(qo-4BYy?CNu$R_z4r|ND6X0N&yzO2vN&rH+c
zMwW3z|ALdps={BT9X`D0LALySNrl_BexDXQ{Pz2L>yXtqiMOn5i=Vkmt^Lg77`w#o
z<07R)rN`!I8d*=R^Uu!+KRWTuaesedg84b2{B;*+Mucwep0nsc>?6Mk
zd1=ACUmg6988qf;RR5i%QWThL&A`RHIPcVkJy~LfVjWfX8s~p1+q_I*nB?gB-b2E;
z=DaL#`ZP1e-Sr1Jc1Sl`+|TE_%3ilJYwEGhDE!zbR~s5WHk
z@p@InyQ5&`^;tP;t2q*1B|bVJ*|SLhXT#x{jQ>0reJpG?65{GDUNMt3mi_fYhrmOEb&wj$$j@R;(x8k>jaj!N--nSIG|LiW?
zox^dj7}RGf9X+wUEh?*Kk-mGX`=;HV&YHgYds+U;*DuZ69G7V)JlEP%{kKPx;jjA1
zcE&-6GUk7-33OhxbJvdWrN;BWRGOzZ&;G#mF7Xw^^s_nX{Lz*y7F#~s|IXW^73u;Pcm1C`nL%+$#Sc--%k|&gYJGGjr%an!^mD3*M83+C8M&|L
zpPnVJSvqy0sIgk7v{1pS*-T5GNIz3KvR(Ol<2Maw4HY-V*cS&A7N2<|*QZ;vFYyj<
z;~A~#>NWyjQZij^zS{lIzO%EfvXQs@+nobb)|f15efd=2^&B0;jM$2)!P>shc3(9*
zS*sO$=!be~{k+2RAD?f_SQWPL@uaBCH!rqGz4x)$wB6ZDZu*9K_0u_xru|P4R#sBq
zXI=1Itn8C=m&OuX=bu{qA2|1OH?A~YtmE6(`bnj{tt>+1UyNqvMOTQj^W$}!!{Ys~>{D~?tln}=
z=6v?X*I_&O&2l+8D-T>qxp*WZdFtn_HTmB@YOUT}cXwvZtibb@CEN^)1dm17&JXGF
zyQ+L+)eFBk-j~1TTxY0!ey@I&%%|P}ZS(cEw;sGZz3OrCU8VA=ZhD0~XBYQd`wK7O
zvkKJ~GM={Yv-{U`=gWIz=k&1n2bQoK8&z%7F3;4?eYlLNsO}jXx5Fu0Q?IM)+}F&v
z=3a@bpQ`!p>@NLBGV0z!8>8=9-&wqJ+N->YCq)?zFRraQexu;1$4{l1>#}DYsei|F
zqh#NaTh~@H?-9^={@$$Y;vfC+n@6($dc6D7U6okxdHU7s`tup(fm0^OFFL3H)TX@m
z7_-r$1)}c`X0_WEIw=lpj(Y>e*DI51l?EcTehJa5Sj@{huf{ocCWm*Haap7c9a
zVRx^ju5{b4y}f)LSJ{mfsk?t#P2VZMXhpQ?t@?s$@4HK9?+7{XZ?kZ>u<1$XnvX{k
z%QxRUxZ#()&gvS~rovqfhB+K}^qz~UWv8zAm|Io0{#=5^`Iak3AN}FK|A43VipZj4
ziQis5Sp}w9Eaa+wy~3bk5C;
zl+r9tR$bp+Zynu!vNrX7`&R$ys#eP87pFVD)#2p(s~Z{F1;Q
zT{-RfmQ|9PMLi!yF3yg7QZ!-jp0l&10&6oS-(PvUyjOY3>#Z80{~y`C5jdb*er(
Date: Fri, 30 Sep 2022 10:19:17 -0500
Subject: [PATCH 0117/1058] Fix formatting
---
lnbits/core/templates/core/index.html | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index aa0398cf..146fc6ad 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -180,9 +180,7 @@
>
-
-
-
+
From ca69cb5aaf3429a60db72623efe17ad0b51341ac Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Thu, 6 Oct 2022 17:51:36 +0100
Subject: [PATCH 0118/1058] Revert "make gh workflows only run on pull_request"
---
.github/workflows/codeql.yml | 2 ++
.github/workflows/formatting.yml | 2 ++
.github/workflows/mypy.yml | 2 +-
.github/workflows/regtest.yml | 2 +-
.github/workflows/tests.yml | 2 +-
5 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index bbe95983..876c8b8a 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,6 +1,8 @@
name: codeql
on:
+ push:
+ branches: [main, ]
pull_request:
branches: [main]
schedule:
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
index 21c7fb38..e3d0fd35 100644
--- a/.github/workflows/formatting.yml
+++ b/.github/workflows/formatting.yml
@@ -1,6 +1,8 @@
name: formatting
on:
+ push:
+ branches: [ main ]
pull_request:
branches: [ main ]
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 96b574d2..d80da678 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -1,6 +1,6 @@
name: mypy
-on: [pull_request]
+on: [push, pull_request]
jobs:
check:
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index f0adb3ac..2d7aae6b 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -1,6 +1,6 @@
name: regtest
-on: [pull_request]
+on: [push, pull_request]
jobs:
LndRestWallet:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c7b6e44b..5d368fbb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,6 +1,6 @@
name: tests
-on: [pull_request]
+on: [push, pull_request]
jobs:
venv-sqlite:
From 1a6ac735f10ac5bf3545173fa39e5b8e1eb6fa36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 7 Oct 2022 11:30:28 +0200
Subject: [PATCH 0119/1058] update migrations
---
lnbits/extensions/admin/migrations.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 8f6c76a0..b87f7477 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -5,7 +5,7 @@ async def m001_create_admin_settings_table(db):
lnbits_admin_ui TEXT,
debug TEXT,
host TEXT,
- port INT,
+ port INTEGER,
lnbits_path TEXT,
lnbits_commit TEXT,
lnbits_admin_users TEXT,
@@ -39,6 +39,12 @@ async def m001_create_admin_settings_table(db):
lnd_rest_endpoint TEXT,
lnd_rest_cert TEXT,
lnd_rest_macaroon TEXT,
+ lnd_grpc_endpoint: TEXT,
+ lnd_grpc_cert TEXT,
+ lnd_grpc_port INTEGER,
+ lnd_grpc_admin_macaroon TEXT,
+ lnd_grpc_invoice_macaroon TEXT,
+ lnd_grpc_macaroon_encrypted TEXT,
lnpay_api_endpoint TEXT,
lnpay_api_key TEXT,
lnpay_wallet_key TEXT,
From 2b4b1146f4fa5d64532950f4c5719672646a91bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 7 Oct 2022 11:33:48 +0200
Subject: [PATCH 0120/1058] fixup
---
lnbits/extensions/admin/migrations.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index b87f7477..c4bc98d8 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -39,7 +39,7 @@ async def m001_create_admin_settings_table(db):
lnd_rest_endpoint TEXT,
lnd_rest_cert TEXT,
lnd_rest_macaroon TEXT,
- lnd_grpc_endpoint: TEXT,
+ lnd_grpc_endpoint TEXT,
lnd_grpc_cert TEXT,
lnd_grpc_port INTEGER,
lnd_grpc_admin_macaroon TEXT,
From 14e9df1d3e684648a2b729e06d44187691491d2a Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 13:44:26 +0300
Subject: [PATCH 0121/1058] feat: return promises
---
lnbits/extensions/cashu/core/b_dhke.py | 93 ++++++++++++++++++++++++++
lnbits/extensions/cashu/crud.py | 2 +-
lnbits/extensions/cashu/mint.py | 47 +++++++------
lnbits/extensions/cashu/mint_helper.py | 25 +++----
lnbits/extensions/cashu/models.py | 3 +-
lnbits/extensions/cashu/views_api.py | 34 ++++++----
6 files changed, 158 insertions(+), 46 deletions(-)
create mode 100644 lnbits/extensions/cashu/core/b_dhke.py
diff --git a/lnbits/extensions/cashu/core/b_dhke.py b/lnbits/extensions/cashu/core/b_dhke.py
new file mode 100644
index 00000000..be9a141b
--- /dev/null
+++ b/lnbits/extensions/cashu/core/b_dhke.py
@@ -0,0 +1,93 @@
+# Don't trust me with cryptography.
+
+"""
+Implementation of https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406
+Alice:
+A = a*G
+return A
+Bob:
+Y = hash_to_point(secret_message)
+r = random blinding factor
+B'= Y + r*G
+return B'
+Alice:
+C' = a*B'
+ (= a*Y + a*r*G)
+return C'
+Bob:
+C = C' - r*A
+ (= C' - a*r*G)
+ (= a*Y)
+return C, secret_message
+Alice:
+Y = hash_to_point(secret_message)
+C == a*Y
+If true, C must have originated from Alice
+"""
+
+import hashlib
+
+from secp256k1 import PrivateKey, PublicKey
+
+
+def hash_to_point(secret_msg):
+ """Generates x coordinate from the message hash and checks if the point lies on the curve.
+ If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
+ point = None
+ msg = secret_msg
+ while point is None:
+ _hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
+ try:
+ # We construct compressed pub which has x coordinate encoded with even y
+ _hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
+ _hash[0] = 0x02 # set first byte to represent even y coord
+ _hash = bytes(_hash)
+ point = PublicKey(_hash, raw=True)
+ except:
+ msg = _hash
+
+ return point
+
+
+def step1_alice(secret_msg):
+ secret_msg = secret_msg.encode("utf-8")
+ Y = hash_to_point(secret_msg)
+ r = PrivateKey()
+ B_ = Y + r.pubkey
+ return B_, r
+
+
+def step2_bob(B_, a):
+ C_ = B_.mult(a)
+ return C_
+
+
+def step3_alice(C_, r, A):
+ C = C_ - A.mult(r)
+ return C
+
+
+def verify(a, C, secret_msg):
+ Y = hash_to_point(secret_msg.encode("utf-8"))
+ return C == Y.mult(a)
+
+
+### Below is a test of a simple positive and negative case
+
+# # Alice's keys
+# a = PrivateKey()
+# A = a.pubkey
+# secret_msg = "test"
+# B_, r = step1_alice(secret_msg)
+# C_ = step2_bob(B_, a)
+# C = step3_alice(C_, r, A)
+# print("C:{}, secret_msg:{}".format(C, secret_msg))
+# assert verify(a, C, secret_msg)
+# assert verify(a, C + C, secret_msg) == False # adding C twice shouldn't pass
+# assert verify(a, A, secret_msg) == False # A shouldn't pass
+
+# # Test operations
+# b = PrivateKey()
+# B = b.pubkey
+# assert -A -A + A == -A # neg
+# assert B.mult(a) == A.mult(b) # a*B = A*b
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 733f4737..39af1f8c 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -93,7 +93,7 @@ async def delete_cashu(cashu_id) -> None:
##########################################
-async def store_promise(amount: int, B_: str, C_: str, cashu_id):
+async def store_promise(amount: int, B_: str, C_: str, cashu_id: str):
promise_id = urlsafe_short_hash()
await db.execute(
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index c2568313..b1bdd042 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -1,5 +1,12 @@
-from .mint_helper import derive_keys, derive_pubkeys
-from .models import Cashu
+from typing import List
+
+from .core.b_dhke import step2_bob
+from .core.base import BlindedSignature
+from .core.secp import PublicKey
+from .mint_helper import derive_key, derive_keys, derive_pubkeys
+
+# todo: extract const
+MAX_ORDER = 64
def get_pubkeys(xpriv: str):
@@ -11,22 +18,24 @@ def get_pubkeys(xpriv: str):
return {a: p.serialize().hex() for a, p in pub_keys.items()}
-# async def mint(self, B_s: List[PublicKey], amounts: List[int], payment_hash=None):
-# """Mints a promise for coins for B_."""
-# # check if lightning invoice was paid
-# if LIGHTNING:
-# try:
-# paid = await self._check_lightning_invoice(payment_hash)
-# except:
-# raise Exception("could not check invoice.")
-# if not paid:
-# raise Exception("Lightning invoice not paid yet.")
+async def generate_promises(
+ master_prvkey: str, amounts: List[int], B_s: List[PublicKey]
+):
+ """Mints a promise for coins for B_."""
-# for amount in amounts:
-# if amount not in [2**i for i in range(MAX_ORDER)]:
-# raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
+ for amount in amounts:
+ if amount not in [2**i for i in range(MAX_ORDER)]:
+ raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
-# promises = [
-# await self._generate_promise(amount, B_) for B_, amount in zip(B_s, amounts)
-# ]
-# return promises
+ promises = [
+ await generate_promise(master_prvkey, amount, B_)
+ for B_, amount in zip(B_s, amounts)
+ ]
+ return promises
+
+
+async def generate_promise(master_prvkey: str, amount: int, B_: PublicKey):
+ """Generates a promise for given amount and returns a pair (amount, C')."""
+ secret_key = derive_key(master_prvkey, amount) # Get the correct key
+ C_ = step2_bob(B_, secret_key)
+ return BlindedSignature(amount=amount, C_=C_.serialize().hex())
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 1cf631b4..50227733 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -1,7 +1,7 @@
import hashlib
-from typing import List, Set
+from typing import List
-from .core.secp import PrivateKey, PublicKey
+from .core.secp import PrivateKey
# todo: extract const
MAX_ORDER = 64
@@ -9,16 +9,17 @@ MAX_ORDER = 64
def derive_keys(master_key: str):
"""Deterministic derivation of keys for 2^n values."""
- return {
- 2
- ** i: PrivateKey(
- hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
- .hexdigest()
- .encode("utf-8")[:32],
- raw=True,
- )
- for i in range(MAX_ORDER)
- }
+ return {2**i: derive_key(master_key, i) for i in range(MAX_ORDER)}
+
+
+def derive_key(master_key: str, i: int):
+ """Deterministic derivation of keys for a particular value."""
+ return PrivateKey(
+ hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")[:32],
+ raw=True,
+ )
def derive_pubkeys(keys: List[PrivateKey]):
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 8b5a3417..8b7558d2 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -147,7 +147,8 @@ class MeltPayload(BaseModel):
amount: int
invoice: str
+
class CreateTokens(BaseModel):
# cashu_id: str = Query(None)
payloads: MintPayloads
- payment_hash: Union[str, None] = None
\ No newline at end of file
+ payment_hash: Union[str, None] = None
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 6f1e161a..93902356 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -24,9 +24,10 @@ from .crud import (
get_cashus,
get_lightning_invoice,
store_lightning_invoice,
+ store_promise,
)
from .ledger import mint, request_mint
-from .mint import get_pubkeys
+from .mint import generate_promises, get_pubkeys
from .models import (
Cashu,
CheckPayload,
@@ -280,22 +281,29 @@ async def mint_coins(
# if invoice.issued == True:
# todo: give old tokens?
- status: PaymentStatus = await check_transaction_status(cashu.wallet, data.payment_hash)
- if status.paid != True:
+ status: PaymentStatus = await check_transaction_status(
+ cashu.wallet, data.payment_hash
+ )
+ # todo: revert to: status.paid != True:
+ if status.paid == False:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
- # 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))
+ amounts = []
+ B_s = []
+ for payload in data.payloads.blinded_messages:
+ amounts.append(payload.amount)
+ B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
+ print("### amounts", amounts)
+ print("### B_s", B_s)
+
+ try:
+ promises = await generate_promises(cashu.prvkey, amounts, B_s)
+ # await store_promise(amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), cashu_id)
+ return promises
+ except Exception as exc:
+ return CashuError(error=str(exc))
@cashu_ext.post("/melt")
From 8f32e56b717f25fec2a9236216132962478890d6 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 14:51:06 +0300
Subject: [PATCH 0122/1058] fix: don't even try to refactor
---
lnbits/extensions/cashu/crud.py | 5 +++++
lnbits/extensions/cashu/mint.py | 4 ++--
lnbits/extensions/cashu/mint_helper.py | 20 ++++++++++----------
lnbits/extensions/cashu/views_api.py | 3 +++
4 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 39af1f8c..698a8089 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -93,6 +93,11 @@ async def delete_cashu(cashu_id) -> None:
##########################################
+async def store_promises(amounts: List[int], B_s: List[str], C_s: List[str], cashu_id: str):
+ for amount, B_, C_ in zip(amounts, B_s, C_s):
+ await store_promise(amount, B_, C_, cashu_id)
+
+
async def store_promise(amount: int, B_: str, C_: str, cashu_id: str):
promise_id = urlsafe_short_hash()
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index b1bdd042..1be07b91 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -3,7 +3,7 @@ from typing import List
from .core.b_dhke import step2_bob
from .core.base import BlindedSignature
from .core.secp import PublicKey
-from .mint_helper import derive_key, derive_keys, derive_pubkeys
+from .mint_helper import derive_keys, derive_pubkeys
# todo: extract const
MAX_ORDER = 64
@@ -36,6 +36,6 @@ async def generate_promises(
async def generate_promise(master_prvkey: str, amount: int, B_: PublicKey):
"""Generates a promise for given amount and returns a pair (amount, C')."""
- secret_key = derive_key(master_prvkey, amount) # Get the correct key
+ secret_key = derive_keys(master_prvkey)[amount] # Get the correct key
C_ = step2_bob(B_, secret_key)
return BlindedSignature(amount=amount, C_=C_.serialize().hex())
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 50227733..cfb3b7d7 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -9,18 +9,18 @@ MAX_ORDER = 64
def derive_keys(master_key: str):
"""Deterministic derivation of keys for 2^n values."""
- return {2**i: derive_key(master_key, i) for i in range(MAX_ORDER)}
+ return {
+ 2
+ ** i: PrivateKey(
+ hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")[:32],
+ raw=True,
+ )
+ for i in range(MAX_ORDER)
+ }
-def derive_key(master_key: str, i: int):
- """Deterministic derivation of keys for a particular value."""
- return PrivateKey(
- hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
- .hexdigest()
- .encode("utf-8")[:32],
- raw=True,
- )
-
def derive_pubkeys(keys: List[PrivateKey]):
return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 93902356..fb4cefb0 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -300,6 +300,9 @@ async def mint_coins(
try:
promises = await generate_promises(cashu.prvkey, amounts, B_s)
+ for amount, B_, p in zip(amounts, B_s, promises):
+ await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
+ # store_promises(amounts, B_s, C_s, cashu_id)
# await store_promise(amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), cashu_id)
return promises
except Exception as exc:
From 9b5a8cf6f43a69eca9b72cf33d81bad1bc93b52e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 14:53:13 +0300
Subject: [PATCH 0123/1058] fix: uniq promise
---
lnbits/extensions/cashu/migrations.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index cb6b24f9..d06baef1 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -43,6 +43,7 @@ async def m001_initial(db):
B_b TEXT NOT NULL,
C_b TEXT NOT NULL,
cashu_id TEXT NOT NULL
+ UNIQUE (B_b)
);
"""
)
From 94aca31c32544198c4f874462f0654248f8d2719 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 14:57:12 +0300
Subject: [PATCH 0124/1058] chore: code clean-up
---
lnbits/extensions/cashu/views_api.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index fb4cefb0..2379aec6 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -295,15 +295,11 @@ async def mint_coins(
for payload in data.payloads.blinded_messages:
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
- print("### amounts", amounts)
- print("### B_s", B_s)
try:
promises = await generate_promises(cashu.prvkey, amounts, B_s)
for amount, B_, p in zip(amounts, B_s, promises):
await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
- # store_promises(amounts, B_s, C_s, cashu_id)
- # await store_promise(amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), cashu_id)
return promises
except Exception as exc:
return CashuError(error=str(exc))
From 03313e975cbfc43aa982343c9e19c16852316c48 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 15:09:26 +0300
Subject: [PATCH 0125/1058] fix: mint format
---
lnbits/extensions/cashu/views_api.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 2379aec6..5bbf084e 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -255,8 +255,9 @@ async def mint_pay_request(
@cashu_ext.post("/api/v1/mint/{cashu_id}")
async def mint_coins(
- data: CreateTokens,
+ data: MintPayloads,
cashu_id: str = Query(None),
+ payment_hash: Union[str, None] = None,
wallet: WalletTypeInfo = Depends(require_admin_key),
):
"""
@@ -271,8 +272,8 @@ async def mint_coins(
)
invoice: Invoice = (
None
- if data.payment_hash == None
- else await get_lightning_invoice(cashu_id, data.payment_hash)
+ if payment_hash == None
+ else await get_lightning_invoice(cashu_id, payment_hash)
)
if invoice is None:
raise HTTPException(
@@ -282,7 +283,7 @@ async def mint_coins(
# todo: give old tokens?
status: PaymentStatus = await check_transaction_status(
- cashu.wallet, data.payment_hash
+ cashu.wallet, payment_hash
)
# todo: revert to: status.paid != True:
if status.paid == False:
@@ -292,7 +293,7 @@ async def mint_coins(
amounts = []
B_s = []
- for payload in data.payloads.blinded_messages:
+ for payload in data.blinded_messages:
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
From c8d5a0cae47fd39e850210dd74d484121ab1d9f3 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 15:18:09 +0300
Subject: [PATCH 0126/1058] feat: update invoice status
---
lnbits/extensions/cashu/views_api.py | 32 +++++++++++++++++-----------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 5bbf084e..d6b34538 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -25,6 +25,7 @@ from .crud import (
get_lightning_invoice,
store_lightning_invoice,
store_promise,
+ update_lightning_invoice,
)
from .ledger import mint, request_mint
from .mint import generate_promises, get_pubkeys
@@ -279,8 +280,10 @@ async def mint_coins(
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not have this invoice."
)
- # if invoice.issued == True:
- # todo: give old tokens?
+ if invoice.issued == True:
+ raise HTTPException(
+ status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Tokens already issued for this invoice."
+ )
status: PaymentStatus = await check_transaction_status(
cashu.wallet, payment_hash
@@ -290,18 +293,21 @@ async def mint_coins(
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
-
- amounts = []
- B_s = []
- for payload in data.blinded_messages:
- amounts.append(payload.amount)
- B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
-
try:
- promises = await generate_promises(cashu.prvkey, amounts, B_s)
- for amount, B_, p in zip(amounts, B_s, promises):
- await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
- return promises
+ await update_lightning_invoice(cashu_id, payment_hash, True)
+
+ amounts = []
+ B_s = []
+ for payload in data.blinded_messages:
+ amounts.append(payload.amount)
+ B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
+
+
+ promises = await generate_promises(cashu.prvkey, amounts, B_s)
+ for amount, B_, p in zip(amounts, B_s, promises):
+ await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
+
+ return promises
except Exception as exc:
return CashuError(error=str(exc))
From 01877d3b1d11a6f434816cc313472209de2064af Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 17:44:02 +0300
Subject: [PATCH 0127/1058] feat: melt tokens
---
lnbits/extensions/cashu/crud.py | 12 ++---
lnbits/extensions/cashu/mint.py | 62 ++++++++++++++++++++++++--
lnbits/extensions/cashu/mint_helper.py | 21 +++++++--
lnbits/extensions/cashu/models.py | 6 ---
lnbits/extensions/cashu/views_api.py | 41 ++++++++++-------
5 files changed, 109 insertions(+), 33 deletions(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 698a8089..c8f3c72b 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -93,7 +93,9 @@ async def delete_cashu(cashu_id) -> None:
##########################################
-async def store_promises(amounts: List[int], B_s: List[str], C_s: List[str], cashu_id: str):
+async def store_promises(
+ amounts: List[int], B_s: List[str], C_s: List[str], cashu_id: str
+):
for amount, B_, C_ in zip(amounts, B_s, C_s):
await store_promise(amount, B_, C_, cashu_id)
@@ -113,21 +115,21 @@ async def store_promise(amount: int, B_: str, C_: str, cashu_id: str):
async def get_promises(cashu_id) -> Optional[Cashu]:
row = await db.fetchall(
- "SELECT * FROM cashu.promises WHERE cashu_id = ?", (promises_id,)
+ "SELECT * FROM cashu.promises WHERE cashu_id = ?", (cashu_id,)
)
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 id = ?", (cashu_id,)
+ "SELECT secret from cashu.proofs_used WHERE cashu_id = ?", (cashu_id,)
)
return [row[0] for row in rows]
-async def invalidate_proof(proof: Proof, cashu_id):
+async def invalidate_proof(cashu_id: str, proof: Proof):
invalidate_proof_id = urlsafe_short_hash()
- await (conn or db).execute(
+ await db.execute(
"""
INSERT INTO cashu.proofs_used
(id, amount, C, secret, cashu_id)
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index 1be07b91..6be60703 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -1,9 +1,16 @@
-from typing import List
+import math
+from typing import List, Set
+
+from lnbits import bolt11
+from lnbits.core.services import check_transaction_status, fee_reserve, pay_invoice
+from lnbits.extensions.cashu.models import Cashu
+from lnbits.wallets.base import PaymentStatus
from .core.b_dhke import step2_bob
-from .core.base import BlindedSignature
+from .core.base import BlindedSignature, Proof
from .core.secp import PublicKey
-from .mint_helper import derive_keys, derive_pubkeys
+from .crud import get_proofs_used, invalidate_proof
+from .mint_helper import derive_keys, derive_pubkeys, verify_proof
# todo: extract const
MAX_ORDER = 64
@@ -39,3 +46,52 @@ async def generate_promise(master_prvkey: str, amount: int, B_: PublicKey):
secret_key = derive_keys(master_prvkey)[amount] # Get the correct key
C_ = step2_bob(B_, secret_key)
return BlindedSignature(amount=amount, C_=C_.serialize().hex())
+
+
+async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
+ """Invalidates proofs and pays a Lightning invoice."""
+ # Verify proofs
+ proofs_used: Set[str] = set(await get_proofs_used(cashu.id))
+ # if not all([verify_proof(cashu.prvkey, proofs_used, p) for p in proofs]):
+ # raise Exception("could not verify proofs.")
+ for p in proofs:
+ await verify_proof(cashu.prvkey, proofs_used, p)
+
+ total_provided = sum([p["amount"] for p in proofs])
+ invoice_obj = bolt11.decode(invoice)
+ amount = math.ceil(invoice_obj.amount_msat / 1000)
+
+ fees_msat = await check_fees(cashu.wallet, invoice_obj)
+ assert total_provided >= amount + fees_msat / 1000, Exception(
+ "provided proofs not enough for Lightning payment."
+ )
+
+ 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 invalidate_proofs(cashu.id, proofs)
+ return status.paid, status.preimage
+ return False, ""
+
+
+async def check_fees(wallet_id: str, decoded_invoice):
+ """Returns the fees (in msat) required to pay this pr."""
+ amount = math.ceil(decoded_invoice.amount_msat / 1000)
+ status: PaymentStatus = await check_transaction_status(
+ wallet_id, decoded_invoice.payment_hash
+ )
+ fees_msat = fee_reserve(amount * 1000) if status.paid != True else 0
+ return fees_msat
+
+async def invalidate_proofs(cashu_id: str, proofs: List[Proof]):
+ """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
+ for p in proofs:
+ await invalidate_proof(cashu_id, p)
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index cfb3b7d7..5fc43e49 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -1,7 +1,9 @@
import hashlib
-from typing import List
+from typing import List, Set
-from .core.secp import PrivateKey
+from .core.b_dhke import verify
+from .core.secp import PrivateKey, PublicKey
+from .models import Proof
# todo: extract const
MAX_ORDER = 64
@@ -21,6 +23,19 @@ def derive_keys(master_key: str):
}
-
def derive_pubkeys(keys: List[PrivateKey]):
return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
+
+
+async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
+ """Verifies that the proof of promise was issued by this ledger."""
+ if proof.secret in proofs_used:
+ raise Exception(f"tokens already spent. Secret: {proof.secret}")
+
+ secret_key = derive_keys(master_prvkey)[
+ proof.amount
+ ] # Get the correct key to check against
+ C = PublicKey(bytes.fromhex(proof.C), raw=True)
+ validMintSig = verify(secret_key, C, proof.secret)
+ if validMintSig != True:
+ raise Exception(f"tokens not valid. Secret: {proof.secret}")
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 8b7558d2..596db047 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -146,9 +146,3 @@ class MeltPayload(BaseModel):
proofs: List[Proof]
amount: int
invoice: str
-
-
-class CreateTokens(BaseModel):
- # cashu_id: str = Query(None)
- payloads: MintPayloads
- payment_hash: Union[str, None] = None
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index d6b34538..bd1d27f0 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -28,11 +28,10 @@ from .crud import (
update_lightning_invoice,
)
from .ledger import mint, request_mint
-from .mint import generate_promises, get_pubkeys
+from .mint import generate_promises, get_pubkeys, melt
from .models import (
Cashu,
CheckPayload,
- CreateTokens,
Invoice,
MeltPayload,
MintPayloads,
@@ -248,7 +247,7 @@ async def mint_pay_request(
except Exception as e:
logger.error(e)
raise HTTPException(
- status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(cashu_id)
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)
)
return {"pr": payment_request, "hash": payment_hash}
@@ -265,7 +264,6 @@ async def mint_coins(
Requests the minting of tokens belonging to a paid payment request.
Call this endpoint after `GET /mint`.
"""
- print("############################ amount")
cashu: Cashu = await get_cashu(cashu_id)
if cashu is None:
raise HTTPException(
@@ -282,12 +280,11 @@ async def mint_coins(
)
if invoice.issued == True:
raise HTTPException(
- status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Tokens already issued for this invoice."
+ status_code=HTTPStatus.PAYMENT_REQUIRED,
+ detail="Tokens already issued for this invoice.",
)
- 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 == False:
raise HTTPException(
@@ -302,21 +299,33 @@ async def mint_coins(
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
-
promises = await generate_promises(cashu.prvkey, amounts, B_s)
for amount, B_, p in zip(amounts, B_s, promises):
await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
-
+
return promises
- except Exception as exc:
- return CashuError(error=str(exc))
+ except Exception as e:
+ logger.error(e)
+ raise HTTPException(
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)
+ )
-@cashu_ext.post("/melt")
+@cashu_ext.post("/api/v1/melt/{cashu_id}")
async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
-
- ok, preimage = await melt(payload.proofs, payload.amount, payload.invoice, cashu_id)
- return {"paid": ok, "preimage": preimage}
+ cashu: Cashu = await get_cashu(cashu_id)
+ if cashu is None:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+ )
+ try:
+ ok, preimage = await melt(cashu, payload.proofs, payload.invoice)
+ return {"paid": ok, "preimage": preimage}
+ except Exception as e:
+ logger.error(e)
+ raise HTTPException(
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)
+ )
@cashu_ext.post("/check")
From 20ca11a00451cd71eda32087bc4ebf555fa9df47 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 18:18:46 +0300
Subject: [PATCH 0128/1058] feat: make endpoints compatible with `cashu` client
---
lnbits/extensions/cashu/views_api.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index bd1d27f0..5ba80eaa 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -206,7 +206,7 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
########################################
-@cashu_ext.get("/api/v1/mint/keys/{cashu_id}", status_code=HTTPStatus.OK)
+@cashu_ext.get("/api/v1/mint/{cashu_id}/keys", status_code=HTTPStatus.OK)
async def keys(
cashu_id: str = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -219,7 +219,7 @@ async def keys(
return get_pubkeys(mint.prvkey)
-@cashu_ext.get("/api/v1/mint/{cashu_id}")
+@cashu_ext.get("/api/v1/{cashu_id}/mint")
async def mint_pay_request(
amount: int = 0,
cashu_id: str = Query(None),
@@ -253,7 +253,7 @@ async def mint_pay_request(
return {"pr": payment_request, "hash": payment_hash}
-@cashu_ext.post("/api/v1/mint/{cashu_id}")
+@cashu_ext.post("/api/v1/{cashu_id}/mint")
async def mint_coins(
data: MintPayloads,
cashu_id: str = Query(None),
@@ -311,7 +311,7 @@ async def mint_coins(
)
-@cashu_ext.post("/api/v1/melt/{cashu_id}")
+@cashu_ext.post("/api/v1/{cashu_id}/melt")
async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
cashu: Cashu = await get_cashu(cashu_id)
if cashu is None:
From 0555b4c074546871d27bf1e8e648948c9fbc1223 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 19:44:06 +0300
Subject: [PATCH 0129/1058] fix: enpoints
---
lnbits/extensions/cashu/views_api.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 5ba80eaa..25298972 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -82,11 +82,11 @@ async def api_cashu_delete(
if not cashu:
raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ 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 TPoS.")
+ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu.")
await delete_cashu(cashu_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
@@ -206,7 +206,7 @@ async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
########################################
-@cashu_ext.get("/api/v1/mint/{cashu_id}/keys", status_code=HTTPStatus.OK)
+@cashu_ext.get("/api/v1/cashu/{cashu_id}/keys", status_code=HTTPStatus.OK)
async def keys(
cashu_id: str = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -219,7 +219,7 @@ async def keys(
return get_pubkeys(mint.prvkey)
-@cashu_ext.get("/api/v1/{cashu_id}/mint")
+@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
async def mint_pay_request(
amount: int = 0,
cashu_id: str = Query(None),
@@ -253,7 +253,7 @@ async def mint_pay_request(
return {"pr": payment_request, "hash": payment_hash}
-@cashu_ext.post("/api/v1/{cashu_id}/mint")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
async def mint_coins(
data: MintPayloads,
cashu_id: str = Query(None),
@@ -286,7 +286,7 @@ async def mint_coins(
status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash)
# todo: revert to: status.paid != True:
- if status.paid == False:
+ if status.paid != True:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
@@ -311,7 +311,7 @@ async def mint_coins(
)
-@cashu_ext.post("/api/v1/{cashu_id}/melt")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/melt")
async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
cashu: Cashu = await get_cashu(cashu_id)
if cashu is None:
From 468d1513ae5af9a47ae164895f707aad5d1fcfcf Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 20:27:40 +0300
Subject: [PATCH 0130/1058] fix: check invoice amount against blinded message
amount
---
lnbits/extensions/cashu/core/base.py | 2 +-
lnbits/extensions/cashu/views_api.py | 15 +++++++++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/cashu/core/base.py b/lnbits/extensions/cashu/core/base.py
index 4943ee24..947da987 100644
--- a/lnbits/extensions/cashu/core/base.py
+++ b/lnbits/extensions/cashu/core/base.py
@@ -4,7 +4,7 @@ from typing import List, Union
from pydantic import BaseModel
-class CashuError(BaseModel):
+class CashuError(BaseException):
code = "000"
error = "CashuError"
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 25298972..a53caaa0 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -284,6 +284,13 @@ async def mint_coins(
detail="Tokens already issued for this invoice.",
)
+ total_requested = sum([bm.amount for bm in data.blinded_messages])
+ if total_requested > invoice.amount:
+ # raise CashuError(error = f"Requested amount to high: {total_requested}. Invoice amount: {invoice.amount}")
+ raise HTTPException(
+ status_code=HTTPStatus.PAYMENT_REQUIRED, detail=f"Requested amount to 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:
@@ -299,11 +306,11 @@ async def mint_coins(
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
- promises = await generate_promises(cashu.prvkey, amounts, B_s)
- for amount, B_, p in zip(amounts, B_s, promises):
- await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
+ promises = await generate_promises(cashu.prvkey, amounts, B_s)
+ for amount, B_, p in zip(amounts, B_s, promises):
+ await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
- return promises
+ return promises
except Exception as e:
logger.error(e)
raise HTTPException(
From ff975db7b57723cc9367dc0805a72c52627e339c Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 7 Oct 2022 18:39:53 +0100
Subject: [PATCH 0131/1058] make UI great again
---
.../admin/templates/admin/_tab_funding.html | 53 ++----
.../admin/templates/admin/_tab_server.html | 14 +-
.../admin/templates/admin/_tab_theme.html | 20 +--
.../admin/templates/admin/_tab_users.html | 15 +-
.../admin/templates/admin/index.html | 157 ++++++++++++------
5 files changed, 141 insertions(+), 118 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index 503f2adc..8b5456f1 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -9,9 +9,9 @@
{%raw%}
- Funding Source: {{data.settings.lnbits_backend_wallet_class}}
+ Funding Source: {{settings.lnbits_backend_wallet_class}}
- Balance: {{data.balance / 1000}} sats
+ Balance: {{balance / 1000}} sats
{%endraw%}
@@ -24,10 +24,9 @@
Active Funding (Requires server restart)
@@ -38,8 +37,7 @@
dense
type="number"
filled
- name="lnbits_reserve_fee_min"
- v-model="data.settings.lnbits_reserve_fee_min"
+ v-model="formData.lnbits_reserve_fee_min"
label="Reserve fee in msats"
>
@@ -49,7 +47,7 @@
type="number"
filled
name="lnbits_reserve_fee_percent"
- v-model="data.settings.lnbits_reserve_fee_percent"
+ v-model="formData.lnbits_reserve_fee_percent"
label="Reserve fee in percent"
step="0.1"
>
@@ -59,49 +57,22 @@
Funding Sources
-
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_server.html b/lnbits/extensions/admin/templates/admin/_tab_server.html
index 7eed90f9..f234f182 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_server.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_server.html
@@ -8,11 +8,11 @@
Server Info
{%raw%}
-
- SQlite: {{data.settings.lnbits_data_folder}}
+
+ SQlite: {{settings.lnbits_data_folder}}
-
- Postgres: {{data.settings.lnbits_database_url}}
+
+ Postgres: {{settings.lnbits_database_url}}
{%endraw%}
@@ -25,7 +25,7 @@
@@ -19,7 +19,7 @@
@@ -28,7 +28,7 @@
Site Description
@@ -51,7 +51,7 @@
@@ -63,10 +63,10 @@
Themes
@@ -75,7 +75,7 @@
Advertisement Slots
{% raw %}
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html
index 414de775..c396ba7d 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_users.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_users.html
@@ -3,15 +3,14 @@
User Management
- Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{%
- endraw %}
+ Super Admin: {% raw %}{{settings.lnbits_admin_users[0]}}{% endraw %}
Admin Users
{% raw %}
Allowed Users
{% raw %}
Admin Extensions
Disabled Extensions
-
@@ -51,6 +66,39 @@
+
+
+
+ TopUp a wallet
+
+
+
+ Cancel
+
+
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}
{% endblock %}
From 3778990000848fc74c23e4fdab3696f73edda4b1 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 7 Oct 2022 19:24:07 +0100
Subject: [PATCH 0132/1058] make saving possible (possible will change in
future)
---
.../admin/templates/admin/index.html | 36 ++++++++++++-------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 72352651..18df16a9 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -121,8 +121,11 @@
created: function () {
this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data
this.balance = +'{{ balance|safe }}'
- this.formData = this.settings //model
+ this.formData = _.clone(this.settings) //model
+ //this.formData.lnbits_ad_space = "hdh"
console.log(this.formData)
+ console.log(_.isEqual(this.settings, this.formData))
+
},
methods: {
addAdminUser() {
@@ -206,18 +209,27 @@
},
updateSettings() {
let data = {
- ...this.settings,
- ...this.formData
+ lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class,
+ lnbits_admin_users: this.formData.lnbits_admin_users.toString(),
+ lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(),
+ lnbits_admin_ext: this.formData.lnbits_admin_ext,
+ lnbits_disabled_ext: this.formData.lnbits_disabled_ext,
+ lnbits_funding_source: this.formData.lnbits_funding_source,
+ lnbits_force_https: this.formData.lnbits_force_https,
+ lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min,
+ lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent,
+ lnbits_service_fee: this.formData.lnbits_service_fee,
+ lnbits_hide_api: this.formData.lnbits_hide_api,
+ lnbits_site_title: this.formData.lnbits_site_title,
+ lnbits_site_tagline: this.formData.lnbits_site_tagline,
+ lnbits_site_description: this.formData.lnbits_site_description,
+ lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name,
+ lnbits_denomination: this.formData.lnbits_denomination,
+ lnbits_theme: this.formData.lnbits_theme,
+ lnbits_custom_logo: this.formData.lnbits_custom_logo,
+ lnbits_ad_space: this.formData.lnbits_ad_space.toString()
}
- /*
- const formElement = document.getElementById('settings_form')
- const formData = new FormData(formElement)
- const data = {}
- formData.forEach((value, key) => (data[key] = value))
- // only for debugging
- for (const [key, value] of formData) {
- console.log(`${key}: ${value}\n`)
- }*/
+ console.log(data)
LNbits.api
.request(
'PUT',
From 3fdafca0a15bb9931fbfe3a61b7a228c91b2d855 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 7 Oct 2022 19:44:03 +0100
Subject: [PATCH 0133/1058] some more refining
---
lnbits/extensions/admin/models.py | 10 +++++-----
.../extensions/admin/templates/admin/_tab_users.html | 2 ++
lnbits/extensions/admin/templates/admin/index.html | 12 +++++++++---
3 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 13a6cd23..45cd990d 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -4,10 +4,10 @@ from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: str = Query(None)
- lnbits_allowed_users: str = Query(None)
- lnbits_admin_ext: str = Query(None)
- lnbits_disabled_ext: str = Query(None)
+ lnbits_admin_users: str = Query(None) #this should be List[str] ??
+ lnbits_allowed_users: str = Query(None) #this should be List[str] ??
+ lnbits_admin_ext: str = Query(None) #this should be List[str] ??
+ lnbits_disabled_ext: str = Query(None) #this should be List[str] ??
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -21,4 +21,4 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: str = Query(None)
+ lnbits_ad_space: str = Query(None) #this should be List[str] ??
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html
index c396ba7d..08b08a62 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_users.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_users.html
@@ -71,6 +71,7 @@
multiple
hint="Extensions only user with admin privileges can use"
label="Admin extensions"
+ :options="g.extensions.map(e => e.name)"
>
@@ -79,6 +80,7 @@
-
+
Date: Sat, 8 Oct 2022 11:38:14 +0300
Subject: [PATCH 0134/1058] feat: add split logic
---
lnbits/extensions/cashu/core/split.py | 8 +++
lnbits/extensions/cashu/mint.py | 72 +++++++++++++++++++++++---
lnbits/extensions/cashu/mint_helper.py | 57 +++++++++++++++++++-
lnbits/extensions/cashu/views_api.py | 67 ++++++++++++------------
4 files changed, 160 insertions(+), 44 deletions(-)
create mode 100644 lnbits/extensions/cashu/core/split.py
diff --git a/lnbits/extensions/cashu/core/split.py b/lnbits/extensions/cashu/core/split.py
new file mode 100644
index 00000000..44b9cf51
--- /dev/null
+++ b/lnbits/extensions/cashu/core/split.py
@@ -0,0 +1,8 @@
+def amount_split(amount):
+ """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
+ bits_amt = bin(amount)[::-1][:-2]
+ rv = []
+ for (pos, bit) in enumerate(bits_amt):
+ if bit == "1":
+ rv.append(2**pos)
+ return rv
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index 6be60703..3388b45a 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -3,14 +3,24 @@ from typing import List, Set
from lnbits import bolt11
from lnbits.core.services import check_transaction_status, fee_reserve, pay_invoice
-from lnbits.extensions.cashu.models import Cashu
from lnbits.wallets.base import PaymentStatus
from .core.b_dhke import step2_bob
-from .core.base import BlindedSignature, Proof
+from .core.base import BlindedMessage, BlindedSignature, Proof
from .core.secp import PublicKey
+from .core.split import amount_split
from .crud import get_proofs_used, invalidate_proof
-from .mint_helper import derive_keys, derive_pubkeys, verify_proof
+from .mint_helper import (
+ derive_keys,
+ derive_pubkeys,
+ verify_equation_balanced,
+ verify_no_duplicates,
+ verify_outputs,
+ verify_proof,
+ verify_secret_criteria,
+ verify_split_amount,
+)
+from .models import Cashu
# todo: extract const
MAX_ORDER = 64
@@ -52,10 +62,8 @@ async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
"""Invalidates proofs and pays a Lightning invoice."""
# Verify proofs
proofs_used: Set[str] = set(await get_proofs_used(cashu.id))
- # if not all([verify_proof(cashu.prvkey, proofs_used, p) for p in proofs]):
- # raise Exception("could not verify proofs.")
for p in proofs:
- await verify_proof(cashu.prvkey, proofs_used, p)
+ await verify_proof(cashu.prvkey, proofs_used, p)
total_provided = sum([p["amount"] for p in proofs])
invoice_obj = bolt11.decode(invoice)
@@ -91,7 +99,57 @@ async def check_fees(wallet_id: str, decoded_invoice):
fees_msat = fee_reserve(amount * 1000) if status.paid != True else 0
return fees_msat
+
+async def split(
+ cashu: Cashu, proofs: List[Proof], amount: int, outputs: List[BlindedMessage]
+):
+ """Consumes proofs and prepares new promises based on the amount split."""
+ total = sum([p.amount for p in proofs])
+
+ # verify that amount is kosher
+ verify_split_amount(amount)
+ # verify overspending attempt
+ if amount > total:
+ raise Exception(
+ f"split amount ({amount}) is higher than the total sum ({total})."
+ )
+
+ # Verify secret criteria
+ if not all([verify_secret_criteria(p) for p in proofs]):
+ raise Exception("secrets do not match criteria.")
+ # verify that only unique proofs and outputs were used
+ if not verify_no_duplicates(proofs, outputs):
+ raise Exception("duplicate proofs or promises.")
+ # verify that outputs have the correct amount
+ if not verify_outputs(total, amount, outputs): # ?
+ raise Exception("split of promises is not as expected.")
+ # Verify proofs
+ # Verify proofs
+ proofs_used: Set[str] = set(await get_proofs_used(cashu.id))
+ for p in proofs:
+ await verify_proof(cashu.prvkey, proofs_used, p)
+
+ # Mark proofs as used and prepare new promises
+ await invalidate_proofs(cashu.id, proofs)
+
+ outs_fst = amount_split(total - amount)
+ outs_snd = amount_split(amount)
+ B_fst = [
+ PublicKey(bytes.fromhex(od.B_), raw=True) for od in outputs[: len(outs_fst)]
+ ]
+ B_snd = [
+ PublicKey(bytes.fromhex(od.B_), raw=True) for od in outputs[len(outs_fst) :]
+ ]
+ # PublicKey(bytes.fromhex(payload.B_), raw=True)
+ prom_fst, prom_snd = await generate_promises(
+ cashu.prvkey, outs_fst, B_fst
+ ), await generate_promises(cashu.prvkey, outs_snd, B_snd)
+ # verify amounts in produced proofs
+ verify_equation_balanced(proofs, prom_fst + prom_snd)
+ return prom_fst, prom_snd
+
+
async def invalidate_proofs(cashu_id: str, proofs: List[Proof]):
"""Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
for p in proofs:
- await invalidate_proof(cashu_id, p)
\ No newline at end of file
+ await invalidate_proof(cashu_id, p)
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 5fc43e49..5c96d831 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -2,8 +2,10 @@ import hashlib
from typing import List, Set
from .core.b_dhke import verify
+from .core.base import BlindedSignature
from .core.secp import PrivateKey, PublicKey
-from .models import Proof
+from .core.split import amount_split
+from .models import BlindedMessage, Proof
# todo: extract const
MAX_ORDER = 64
@@ -27,6 +29,7 @@ def derive_pubkeys(keys: List[PrivateKey]):
return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
+# async required?
async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
"""Verifies that the proof of promise was issued by this ledger."""
if proof.secret in proofs_used:
@@ -38,4 +41,54 @@ async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
C = PublicKey(bytes.fromhex(proof.C), raw=True)
validMintSig = verify(secret_key, C, proof.secret)
if validMintSig != True:
- raise Exception(f"tokens not valid. Secret: {proof.secret}")
+ raise Exception(f"tokens not valid. Secret: {proof.secret}")
+
+
+def verify_split_amount(amount: int):
+ """Split amount like output amount can't be negative or too big."""
+ try:
+ verify_amount(amount)
+ except:
+ # For better error message
+ raise Exception("invalid split amount: " + str(amount))
+
+
+def verify_secret_criteria(proof: Proof):
+ if proof.secret is None or proof.secret == "":
+ raise Exception("no secret in proof.")
+ return True
+
+
+def verify_no_duplicates(proofs: List[Proof], outputs: List[BlindedMessage]):
+ secrets = [p.secret for p in proofs]
+ if len(secrets) != len(list(set(secrets))):
+ return False
+ B_s = [od.B_ for od in outputs]
+ if len(B_s) != len(list(set(B_s))):
+ return False
+ return True
+
+
+def verify_outputs(total: int, amount: int, outputs: List[BlindedMessage]):
+ """Verifies the expected split was correctly computed"""
+ frst_amt, scnd_amt = total - amount, amount # we have two amounts to split to
+ frst_outputs = amount_split(frst_amt)
+ scnd_outputs = amount_split(scnd_amt)
+ expected = frst_outputs + scnd_outputs
+ given = [o.amount for o in outputs]
+ return given == expected
+
+
+def verify_amount(amount: int):
+ """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
+ valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
+ if not valid:
+ raise Exception("invalid amount: " + str(amount))
+ return amount
+
+
+def verify_equation_balanced(proofs: List[Proof], outs: List[BlindedSignature]):
+ """Verify that Σoutputs - Σinputs = 0."""
+ sum_inputs = sum(verify_amount(p.amount) for p in proofs)
+ sum_outputs = sum(verify_amount(p.amount) for p in outs)
+ assert sum_outputs - sum_inputs == 0
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index a53caaa0..3d221a17 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -1,3 +1,4 @@
+import json
from http import HTTPStatus
from typing import Union
@@ -16,7 +17,7 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.wallets.base import PaymentStatus
from . import cashu_ext
-from .core.base import CashuError
+from .core.base import CashuError, PostSplitResponse, SplitRequest
from .crud import (
create_cashu,
delete_cashu,
@@ -28,7 +29,7 @@ from .crud import (
update_lightning_invoice,
)
from .ledger import mint, request_mint
-from .mint import generate_promises, get_pubkeys, melt
+from .mint import generate_promises, get_pubkeys, melt, split
from .models import (
Cashu,
CheckPayload,
@@ -207,9 +208,7 @@ 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), wallet: WalletTypeInfo = Depends(get_key_type)
-):
+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:
@@ -220,11 +219,7 @@ async def keys(
@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
-async def mint_pay_request(
- amount: int = 0,
- cashu_id: str = Query(None),
- wallet: WalletTypeInfo = Depends(get_key_type),
-):
+async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
"""Request minting of tokens. Server responds with a Lightning invoice."""
cashu = await get_cashu(cashu_id)
@@ -246,9 +241,7 @@ async def mint_pay_request(
await store_lightning_invoice(cashu_id, invoice)
except Exception as 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))
return {"pr": payment_request, "hash": payment_hash}
@@ -258,7 +251,6 @@ async def mint_coins(
data: MintPayloads,
cashu_id: str = Query(None),
payment_hash: Union[str, None] = None,
- wallet: WalletTypeInfo = Depends(require_admin_key),
):
"""
Requests the minting of tokens belonging to a paid payment request.
@@ -286,17 +278,17 @@ async def mint_coins(
total_requested = sum([bm.amount for bm in data.blinded_messages])
if total_requested > invoice.amount:
- # raise CashuError(error = f"Requested amount to high: {total_requested}. Invoice amount: {invoice.amount}")
raise HTTPException(
- status_code=HTTPStatus.PAYMENT_REQUIRED, detail=f"Requested amount to high: {total_requested}. Invoice amount: {invoice.amount}"
+ 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."
- )
+ # if status.paid != True:
+ # raise HTTPException(
+ # status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
+ # )
try:
await update_lightning_invoice(cashu_id, payment_hash, True)
@@ -313,13 +305,12 @@ async def mint_coins(
return promises
except Exception as 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")
async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
+ """Invalidates proofs and pays a Lightning invoice."""
cashu: Cashu = await get_cashu(cashu_id)
if cashu is None:
raise HTTPException(
@@ -330,9 +321,7 @@ async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
return {"paid": ok, "preimage": preimage}
except Exception as 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("/check")
@@ -340,21 +329,29 @@ async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(Non
return await check_spendable(payload.proofs, cashu_id)
-@cashu_ext.post("/split")
-async def spli_coinst(payload: SplitPayload, cashu_id: str = Query(None)):
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
+async def split_proofs(payload: SplitRequest, cashu_id: str = Query(None)):
"""
Requetst a set of tokens with amount "total" to be split into two
newly minted sets with amount "split" and "total-split".
"""
+ print("### RECEIVE")
+ print("payload", json.dumps(payload, default=vars))
+ cashu: 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
amount = payload.amount
- output_data = payload.output_data.blinded_messages
+ outputs = payload.outputs.blinded_messages if payload.outputs else None
try:
- split_return = await split(proofs, amount, output_data)
+ split_return = await split(cashu, proofs, amount, outputs)
except Exception as exc:
- return {"error": str(exc)}
+ raise CashuError(error=str(exc))
if not split_return:
- """There was a problem with the split"""
- raise Exception("could not split tokens.")
- fst_promises, snd_promises = split_return
- return {"fst": fst_promises, "snd": snd_promises}
+ return {"error": "there was a problem with the split."}
+ frst_promises, scnd_promises = split_return
+ resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
+ print("### resp", json.dumps(resp, default=vars))
+ return resp
From c921dde066742503751b03e8a575ad50adf93665 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 11:51:07 +0300
Subject: [PATCH 0135/1058] chore: fix unused endpoint path
---
lnbits/extensions/cashu/views_api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 3d221a17..e2410909 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -324,7 +324,7 @@ async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-@cashu_ext.post("/check")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/check")
async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(None)):
return await check_spendable(payload.proofs, cashu_id)
From bcbe66d607c6deacec445a4a731318a1ab6cd120 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 12:30:29 +0300
Subject: [PATCH 0136/1058] feat: random ui update
---
.../cashu/templates/cashu/wallet.html | 41 +++++++++++++++----
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 75dff22e..3fa8028c 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -7,12 +7,38 @@ page_container %}
-
-
- {% raw %} {{balanceAmount}} {{tickershort}}{%
- endraw %}
-
-
+
+
+ Buy tokens
+ (with sats)
+
+
+
+
+
+ {% raw %} {{balanceAmount}}
+ {{tickershort}}{% endraw %}
+
+
+
+
+ Sell tokens
+ (for sats)
+
+
+
@@ -38,7 +64,8 @@ page_container %}
color="primary"
class="full-width"
@click="showSendDialog"
- >Send
+ Send
From c4a951eca261f3813398a4242d5aa89806b86386 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 14:21:49 +0300
Subject: [PATCH 0137/1058] feat: show mint invoice dialog
---
.../cashu/templates/cashu/index.html | 2 +-
.../cashu/templates/cashu/wallet.html | 176 ++++++++++++------
2 files changed, 115 insertions(+), 63 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html
index 37dc360e..4e5ba911 100644
--- a/lnbits/extensions/cashu/templates/cashu/index.html
+++ b/lnbits/extensions/cashu/templates/cashu/index.html
@@ -49,7 +49,7 @@
icon="launch"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
- :href="'wallet/?tsh=' + props.row.tickershort + '&mnt=' + hostname + props.row.id + '&nme=' + props.row.name"
+ :href="'wallet/?tsh=' + (props.row.tickershort || '') + '&mint_id=' + props.row.id + '&mint_name=' + props.row.name"
target="_blank"
>Shareable wallet page
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3fa8028c..de5cb7b6 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -14,8 +14,8 @@ page_container %}
rounded
color="secondary"
class="full-width"
- @click="showCamera"
- >Buy tokens
+ @click="showBuyTokensDialog"
+ >Buy tokens
(with sats)
@@ -28,12 +28,7 @@ page_container %}
- Sell tokens
(for sats)
@@ -52,7 +47,6 @@ page_container %}
rounded
color="primary"
class="full-width"
- @click="showParseDialog"
>Receive
@@ -63,7 +57,6 @@ page_container %}
rounded
color="primary"
class="full-width"
- @click="showSendDialog"
>
Send
@@ -75,7 +68,6 @@ page_container %}
color="secondary"
icon="sync_alt"
class="full-width"
- @click="showCamera"
>Peg in/out
@@ -86,7 +78,6 @@ page_container %}
color="secondary"
icon="photo_camera"
class="full-width"
- @click="showCamera"
>scan
@@ -107,23 +98,14 @@ page_container %}
icon="approval"
color="grey"
type="a"
- :href="mint"
+ :href="mintName"
target="_blank"
>{% endraw %}
Mint details
- Export to CSV
+ Export to CSV
-
+
Show chart
@@ -244,12 +226,7 @@ page_container %}
-
Copy invoice
+
Copy invoice
-
-
+
+
@@ -557,18 +530,72 @@ page_container %}
lose the funds.
- Copy wallet URL
+ Copy wallet URL
I understand
+
+
+ {% raw %}
+
+
+
+
+
+ How much would you like to buy?
+
+
+
+
+
+
+
+
+ Copy invoice
+ Request Invoice
+ Close
+
+
+ {% endraw %}
+
@@ -605,6 +632,8 @@ page_container %}
return obj
}
+ Vue.component(VueQrcode.name, VueQrcode)
+
new Vue({
el: '#vue',
mixins: [windowMixin],
@@ -613,6 +642,17 @@ page_container %}
balanceAmount: '',
tickershort: '',
name: '',
+
+ mintId: '',
+ mintName: '',
+ buyTokens: {
+ showDialog: false,
+ amount: 0,
+ memo: '',
+ bolt11: '',
+ hash: ''
+ },
+
receive: {
show: false,
status: 'pending',
@@ -933,6 +973,28 @@ page_container %}
field: 'pending'
})
LNbits.utils.exportCSV(columns, this.payments)
+ },
+
+ /////////////////////////////////// WALLET ///////////////////////////////////
+ showBuyTokensDialog: async function () {
+ this.buyTokens.amount = 0
+ this.buyTokens.bolt11 = ''
+ this.buyTokens.hash = ''
+ this.buyTokens.memo = ''
+ this.buyTokens.showDialog = true
+ },
+ requestInvoice: async function () {
+ try {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.buyTokens.amount}`
+ )
+ console.log('### data', data)
+ this.buyTokens.bolt11 = data.pr
+ this.buyTokens.hash = data.hash
+ } catch (error) {
+ LNbits.utils.notifyApiError(error)
+ }
}
},
watch: {
@@ -940,19 +1002,7 @@ page_container %}
this.fetchBalance()
}
},
- created: function () {
- this.fetchBalance()
- this.fetchPayments()
- LNbits.api
- .request('GET', '/api/v1/currencies')
- .then(response => {
- this.receive.units = ['sat', ...response.data]
- })
- .catch(err => {
- LNbits.utils.notifyApiError(err)
- })
- },
created: function () {
let params = new URL(document.location).searchParams
@@ -975,11 +1025,11 @@ page_container %}
}
// get mint
- if (params.get('mnt')) {
- this.mint = params.get('mnt')
- this.$q.localStorage.set('cashu.mint', params.get('mnt'))
+ if (params.get('mint_id')) {
+ this.mintId = params.get('mint_id')
+ this.$q.localStorage.set('cashu.mint', params.get('mint_id'))
} else if (this.$q.localStorage.getItem('cashu.mint')) {
- this.mint = this.$q.localStorage.getItem('cashu.mint')
+ this.mintId = this.$q.localStorage.getItem('cashu.mint')
} else {
this.$q.notify({
color: 'red',
@@ -988,12 +1038,14 @@ page_container %}
}
// get name
- if (params.get('nme')) {
- this.name = params.get('nme')
- this.$q.localStorage.set('cashu.name', params.get('nme'))
+ if (params.get('mint_name')) {
+ this.mintName = params.get('mint_name')
+ this.$q.localStorage.set('cashu.mintName', params.get('mint_name'))
} else if (this.$q.localStorage.getItem('cashu.name')) {
- this.name = this.$q.localStorage.getItem('cashu.name')
+ this.mintName = this.$q.localStorage.getItem('cashu.name')
}
+ console.log('#### this.mintId', this.mintId)
+ console.log('#### this.mintName', this.mintName)
}
})
From 9231147063d2f7de48a6c4fdaafbc159d3a908df Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 14:28:27 +0300
Subject: [PATCH 0138/1058] feat: ui stuff
---
.../cashu/templates/cashu/wallet.html | 71 +++++++------------
1 file changed, 25 insertions(+), 46 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index de5cb7b6..8d0ec1b7 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -37,54 +37,33 @@ page_container %}
-
-
- Receive
-
-
-
- Send
-
-
- Peg in/out
-
-
-
- scan
-
-
-
-
+
+
+ Receive
+
+
+
+
+ Send
+
+
+
Transactions
From e44f614490004e9d17bd604d1d866bd0d836b99b Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 14:41:45 +0300
Subject: [PATCH 0139/1058] feat: check mobile UI
---
.../extensions/cashu/templates/cashu/wallet.html | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 8d0ec1b7..74837719 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -40,7 +40,7 @@ page_container %}
-
+
Receive
-
-
+
+
-
+
+
-
-
+
@@ -1000,7 +1000,7 @@ page_container %}
}
if (!this.$q.localStorage.getItem('cashu.amount')) {
- this.balanceAmount = 0
+ this.balanceAmount = 112340
}
// get mint
From 67d824b610975c4c3452e591a3f1565f52baa076 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Sat, 8 Oct 2022 14:54:48 +0300
Subject: [PATCH 0140/1058] feat: basic buys table
---
.../cashu/templates/cashu/wallet.html | 46 ++++++++++---------
1 file changed, 25 insertions(+), 21 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 74837719..3eb67861 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -64,7 +64,7 @@ page_container %}
-
+
{% raw %}
@@ -663,13 +662,20 @@ page_container %}
}
},
payments: [],
- paymentsTable: {
+ buysTable: {
columns: [
{
- name: 'note',
+ name: 'amount',
align: 'left',
- label: 'Note',
- field: 'note'
+ label: 'Amount',
+ field: 'amount'
+ },
+ {
+ name: 'memo',
+ align: 'left',
+ label: 'Memo',
+ field: 'memo',
+ sortable: true
},
{
name: 'date',
@@ -679,10 +685,10 @@ page_container %}
sortable: true
},
{
- name: 'amount',
+ name: 'hash',
align: 'right',
- label: 'Amount',
- field: 'amount',
+ label: 'Hash',
+ field: 'hash',
sortable: true
}
],
@@ -707,12 +713,10 @@ page_container %}
formattedBalance: function () {
return this.balance / 100
},
- filteredPayments: function () {
- var q = this.paymentsTable.filter
- if (!q || q === '') return this.payments
-
- return LNbits.utils.search(this.payments, q)
+ tokenBuys: function() {
+ return []
},
+
canPay: function () {
if (!this.parse.invoice) return false
return this.parse.invoice.sat <= this.balance
@@ -1000,7 +1004,7 @@ page_container %}
}
if (!this.$q.localStorage.getItem('cashu.amount')) {
- this.balanceAmount = 112340
+ this.balanceAmount = 0
}
// get mint
From 33522204d6311cf59c7bc7c7b579189f83829e5e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 09:07:15 +0300
Subject: [PATCH 0141/1058] feat: store `tokenBuys`
---
.../cashu/templates/cashu/wallet.html | 240 +++++++++---------
1 file changed, 117 insertions(+), 123 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3eb67861..6cf03b7d 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -110,142 +110,129 @@ page_container %}
:filter="buysTable.filter"
>
{% raw %}
-
-
-
- {{ col.label }}
-
-
-
-
-
- Pending
-
-
-
-
- #{{ props.row.tag }}
-
-
- {{ props.row.memo }}
+ {{props.row.amount}}
-
- {{ props.row.date }}
- {{ props.row.dateFrom }}
+
+
+ {{props.row.memo}}
- {% endraw %}
- {% raw %} {{
- parseFloat(String(props.row.fsat).replaceAll(",", "")) / 100
- }}
+
+ {{props.row.date}}
-
-
- {{ props.row.fsat }}
-
-
- {{ props.row.fee }}
+
+ {{props.row.hash}}
+
-
-
-
-
-
-
- Invoice waiting to be paid
-
-
+
+
+
+
-
- Copy invoice
- Close
-
-
-
- Payment Received
-
+
+ Copy
-
-
- Payment Sent
-
+
+
+ Rescan
-
-
- Outgoing payment pending
-
+
+ History
+
+
+ View Coins
-
-
+
+
+
Note:
+
+
+
+
+ Update
+
+
+
+
+
+
+
+ {{props.row.error}}
+
+
+
+
+
+ Gap limit of 20 addresses exceeded. Other wallets might not
+ detect funds at this address.
+
+
+
+ -->
{% endraw %}
@@ -623,6 +610,7 @@ page_container %}
mintId: '',
mintName: '',
+ tokenBuys: [],
buyTokens: {
showDialog: false,
amount: 0,
@@ -713,9 +701,9 @@ page_container %}
formattedBalance: function () {
return this.balance / 100
},
- tokenBuys: function() {
- return []
- },
+ // tokenBuys: function() {
+ // return []
+ // },
canPay: function () {
if (!this.parse.invoice) return false
@@ -973,8 +961,11 @@ page_container %}
`/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.buyTokens.amount}`
)
console.log('### data', data)
+
this.buyTokens.bolt11 = data.pr
this.buyTokens.hash = data.hash
+ this.tokenBuys.push({...this.buyTokens, date: new Date().toISOString()})
+ localStorage.setItem('cashu.tokenBuys', JSON.stringify(this.tokenBuys))
} catch (error) {
LNbits.utils.notifyApiError(error)
}
@@ -1027,6 +1018,9 @@ page_container %}
} else if (this.$q.localStorage.getItem('cashu.name')) {
this.mintName = this.$q.localStorage.getItem('cashu.name')
}
+
+ this.tokenBuys = JSON.parse(localStorage.getItem('cashu.tokenBuys') || '[]')
+ console.log('#### this.tokenBuys', this.tokenBuys)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
}
From 016534600eafffe4b1f3f09367b2f6e810f51550 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 09:19:53 +0300
Subject: [PATCH 0142/1058] refactor: renamings
---
.../cashu/templates/cashu/wallet.html | 63 ++++++++++++-------
1 file changed, 39 insertions(+), 24 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 6cf03b7d..8e4f9e0a 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -119,7 +119,7 @@ page_container %}
>
{{props.row.amount}}
-
+
{{props.row.memo}}
@@ -129,7 +129,6 @@ page_container %}
{{props.row.hash}}
-
{{props.row.hash}}
-
{% endraw %}
@@ -621,7 +483,6 @@ page_container %}
bolt11: '',
hash: ''
}
-
},
receive: {
From 7eaf28f6fa1eb0bcfea0e4a9e95d568107b458c7 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 09:35:47 +0300
Subject: [PATCH 0144/1058] feat: show status
---
.../extensions/cashu/templates/cashu/wallet.html | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index cdb7996a..8a25a8f7 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -76,6 +76,13 @@ page_container %}
{% raw %}
+
+
+
+ Pending
+
+
+
Date: Mon, 10 Oct 2022 12:42:34 +0300
Subject: [PATCH 0145/1058] feat: stupid random tests
---
lnbits/extensions/cashu/__init__.py | 9 +
lnbits/extensions/cashu/static/js/dhke.js | 31 +
.../cashu/static/js/noble-secp256k1.js | 1179 +++++++++++++++++
lnbits/extensions/cashu/static/js/utils.js | 23 +
.../cashu/templates/cashu/wallet.html | 128 ++
lnbits/extensions/cashu/views_api.py | 8 +-
6 files changed, 1374 insertions(+), 4 deletions(-)
create mode 100644 lnbits/extensions/cashu/static/js/dhke.js
create mode 100644 lnbits/extensions/cashu/static/js/noble-secp256k1.js
create mode 100644 lnbits/extensions/cashu/static/js/utils.js
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index bd7d5513..cb62ffca 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -1,6 +1,7 @@
import asyncio
from fastapi import APIRouter
+from fastapi.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@@ -10,6 +11,14 @@ db = Database("ext_cashu")
cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
+cashu_static_files = [
+ {
+ "path": "/cashu/static",
+ "app": StaticFiles(directory="lnbits/extensions/cashu/static"),
+ "name": "cashu_static",
+ }
+]
+
def cashu_renderer():
return template_renderer(["lnbits/extensions/cashu/templates"])
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
new file mode 100644
index 00000000..73b1bcb8
--- /dev/null
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -0,0 +1,31 @@
+async function hashToCurve(secretMessage) {
+ let point
+ while (!point) {
+ const hash = await nobleSecp256k1.utils.sha256(secretMessage)
+ try {
+ point = nobleSecp256k1.Point.fromHex(hash)
+ } catch (error) {
+ // console.error(error)
+ // const x = bytesToNumber(hash) + ''
+ // const msg = await nobleSecp256k1.utils.sha256(x)
+ secretMessage = await nobleSecp256k1.utils.sha256(hash)
+ // secretMessage = nobleSecp256k1.utils.bytesToHex(msg)
+ }
+ }
+ return point
+}
+
+async function step1Bob(secretMessage) {
+ const Y = await hashToCurve(secretMessage)
+ const randomBlindingFactor = bytesToNumber(
+ nobleSecp256k1.utils.randomPrivateKey()
+ )
+ const P = nobleSecp256k1.Point.fromPrivateKey(randomBlindingFactor)
+ const B_ = Y.add(P)
+ return {B_, randomBlindingFactor}
+}
+
+function step3Bob(C_, r, A) {
+ const C = C_.subtract(A.multiply(r))
+ return C
+}
diff --git a/lnbits/extensions/cashu/static/js/noble-secp256k1.js b/lnbits/extensions/cashu/static/js/noble-secp256k1.js
new file mode 100644
index 00000000..3b24b884
--- /dev/null
+++ b/lnbits/extensions/cashu/static/js/noble-secp256k1.js
@@ -0,0 +1,1179 @@
+;(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined'
+ ? factory(exports)
+ : typeof define === 'function' && define.amd
+ ? define(['exports'], factory)
+ : ((global =
+ typeof globalThis !== 'undefined' ? globalThis : global || self),
+ factory((global.nobleSecp256k1 = {})))
+})(this, function (exports) {
+ 'use strict'
+
+ const _nodeResolve_empty = {}
+
+ const nodeCrypto = /*#__PURE__*/ Object.freeze({
+ __proto__: null,
+ default: _nodeResolve_empty
+ })
+
+ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
+ const _0n = BigInt(0)
+ const _1n = BigInt(1)
+ const _2n = BigInt(2)
+ const _3n = BigInt(3)
+ const _8n = BigInt(8)
+ const POW_2_256 = _2n ** BigInt(256)
+ const CURVE = {
+ a: _0n,
+ b: BigInt(7),
+ P: POW_2_256 - _2n ** BigInt(32) - BigInt(977),
+ n: POW_2_256 - BigInt('432420386565659656852420866394968145599'),
+ h: _1n,
+ Gx: BigInt(
+ '55066263022277343669578718895168534326250603453777594175500187360389116729240'
+ ),
+ Gy: BigInt(
+ '32670510020758816978083085130507043184471273380659243275938904335757337482424'
+ ),
+ beta: BigInt(
+ '0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'
+ )
+ }
+ function weistrass(x) {
+ const {a, b} = CURVE
+ const x2 = mod(x * x)
+ const x3 = mod(x2 * x)
+ return mod(x3 + a * x + b)
+ }
+ const USE_ENDOMORPHISM = CURVE.a === _0n
+ class JacobianPoint {
+ constructor(x, y, z) {
+ this.x = x
+ this.y = y
+ this.z = z
+ }
+ static fromAffine(p) {
+ if (!(p instanceof Point)) {
+ throw new TypeError('JacobianPoint#fromAffine: expected Point')
+ }
+ return new JacobianPoint(p.x, p.y, _1n)
+ }
+ static toAffineBatch(points) {
+ const toInv = invertBatch(points.map(p => p.z))
+ return points.map((p, i) => p.toAffine(toInv[i]))
+ }
+ static normalizeZ(points) {
+ return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine)
+ }
+ equals(other) {
+ if (!(other instanceof JacobianPoint))
+ throw new TypeError('JacobianPoint expected')
+ const {x: X1, y: Y1, z: Z1} = this
+ const {x: X2, y: Y2, z: Z2} = other
+ const Z1Z1 = mod(Z1 ** _2n)
+ const Z2Z2 = mod(Z2 ** _2n)
+ const U1 = mod(X1 * Z2Z2)
+ const U2 = mod(X2 * Z1Z1)
+ const S1 = mod(mod(Y1 * Z2) * Z2Z2)
+ const S2 = mod(mod(Y2 * Z1) * Z1Z1)
+ return U1 === U2 && S1 === S2
+ }
+ negate() {
+ return new JacobianPoint(this.x, mod(-this.y), this.z)
+ }
+ double() {
+ const {x: X1, y: Y1, z: Z1} = this
+ const A = mod(X1 ** _2n)
+ const B = mod(Y1 ** _2n)
+ const C = mod(B ** _2n)
+ const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C))
+ const E = mod(_3n * A)
+ const F = mod(E ** _2n)
+ const X3 = mod(F - _2n * D)
+ const Y3 = mod(E * (D - X3) - _8n * C)
+ const Z3 = mod(_2n * Y1 * Z1)
+ return new JacobianPoint(X3, Y3, Z3)
+ }
+ add(other) {
+ if (!(other instanceof JacobianPoint))
+ throw new TypeError('JacobianPoint expected')
+ const {x: X1, y: Y1, z: Z1} = this
+ const {x: X2, y: Y2, z: Z2} = other
+ if (X2 === _0n || Y2 === _0n) return this
+ if (X1 === _0n || Y1 === _0n) return other
+ const Z1Z1 = mod(Z1 ** _2n)
+ const Z2Z2 = mod(Z2 ** _2n)
+ const U1 = mod(X1 * Z2Z2)
+ const U2 = mod(X2 * Z1Z1)
+ const S1 = mod(mod(Y1 * Z2) * Z2Z2)
+ const S2 = mod(mod(Y2 * Z1) * Z1Z1)
+ const H = mod(U2 - U1)
+ const r = mod(S2 - S1)
+ if (H === _0n) {
+ if (r === _0n) {
+ return this.double()
+ } else {
+ return JacobianPoint.ZERO
+ }
+ }
+ const HH = mod(H ** _2n)
+ const HHH = mod(H * HH)
+ const V = mod(U1 * HH)
+ const X3 = mod(r ** _2n - HHH - _2n * V)
+ const Y3 = mod(r * (V - X3) - S1 * HHH)
+ const Z3 = mod(Z1 * Z2 * H)
+ return new JacobianPoint(X3, Y3, Z3)
+ }
+ subtract(other) {
+ return this.add(other.negate())
+ }
+ multiplyUnsafe(scalar) {
+ const P0 = JacobianPoint.ZERO
+ if (typeof scalar === 'bigint' && scalar === _0n) return P0
+ let n = normalizeScalar(scalar)
+ if (n === _1n) return this
+ if (!USE_ENDOMORPHISM) {
+ let p = P0
+ let d = this
+ while (n > _0n) {
+ if (n & _1n) p = p.add(d)
+ d = d.double()
+ n >>= _1n
+ }
+ return p
+ }
+ let {k1neg, k1, k2neg, k2} = splitScalarEndo(n)
+ let k1p = P0
+ let k2p = P0
+ let d = this
+ while (k1 > _0n || k2 > _0n) {
+ if (k1 & _1n) k1p = k1p.add(d)
+ if (k2 & _1n) k2p = k2p.add(d)
+ d = d.double()
+ k1 >>= _1n
+ k2 >>= _1n
+ }
+ if (k1neg) k1p = k1p.negate()
+ if (k2neg) k2p = k2p.negate()
+ k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z)
+ return k1p.add(k2p)
+ }
+ precomputeWindow(W) {
+ const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1
+ const points = []
+ let p = this
+ let base = p
+ for (let window = 0; window < windows; window++) {
+ base = p
+ points.push(base)
+ for (let i = 1; i < 2 ** (W - 1); i++) {
+ base = base.add(p)
+ points.push(base)
+ }
+ p = base.double()
+ }
+ return points
+ }
+ wNAF(n, affinePoint) {
+ if (!affinePoint && this.equals(JacobianPoint.BASE))
+ affinePoint = Point.BASE
+ const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1
+ if (256 % W) {
+ throw new Error(
+ 'Point#wNAF: Invalid precomputation window, must be power of 2'
+ )
+ }
+ let precomputes = affinePoint && pointPrecomputes.get(affinePoint)
+ if (!precomputes) {
+ precomputes = this.precomputeWindow(W)
+ if (affinePoint && W !== 1) {
+ precomputes = JacobianPoint.normalizeZ(precomputes)
+ pointPrecomputes.set(affinePoint, precomputes)
+ }
+ }
+ let p = JacobianPoint.ZERO
+ let f = JacobianPoint.ZERO
+ const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W)
+ const windowSize = 2 ** (W - 1)
+ const mask = BigInt(2 ** W - 1)
+ const maxNumber = 2 ** W
+ const shiftBy = BigInt(W)
+ for (let window = 0; window < windows; window++) {
+ const offset = window * windowSize
+ let wbits = Number(n & mask)
+ n >>= shiftBy
+ if (wbits > windowSize) {
+ wbits -= maxNumber
+ n += _1n
+ }
+ if (wbits === 0) {
+ let pr = precomputes[offset]
+ if (window % 2) pr = pr.negate()
+ f = f.add(pr)
+ } else {
+ let cached = precomputes[offset + Math.abs(wbits) - 1]
+ if (wbits < 0) cached = cached.negate()
+ p = p.add(cached)
+ }
+ }
+ return {p, f}
+ }
+ multiply(scalar, affinePoint) {
+ let n = normalizeScalar(scalar)
+ let point
+ let fake
+ if (USE_ENDOMORPHISM) {
+ const {k1neg, k1, k2neg, k2} = splitScalarEndo(n)
+ let {p: k1p, f: f1p} = this.wNAF(k1, affinePoint)
+ let {p: k2p, f: f2p} = this.wNAF(k2, affinePoint)
+ if (k1neg) k1p = k1p.negate()
+ if (k2neg) k2p = k2p.negate()
+ k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z)
+ point = k1p.add(k2p)
+ fake = f1p.add(f2p)
+ } else {
+ const {p, f} = this.wNAF(n, affinePoint)
+ point = p
+ fake = f
+ }
+ return JacobianPoint.normalizeZ([point, fake])[0]
+ }
+ toAffine(invZ = invert(this.z)) {
+ const {x, y, z} = this
+ const iz1 = invZ
+ const iz2 = mod(iz1 * iz1)
+ const iz3 = mod(iz2 * iz1)
+ const ax = mod(x * iz2)
+ const ay = mod(y * iz3)
+ const zz = mod(z * iz1)
+ if (zz !== _1n) throw new Error('invZ was invalid')
+ return new Point(ax, ay)
+ }
+ }
+ JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n)
+ JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n)
+ const pointPrecomputes = new WeakMap()
+ class Point {
+ constructor(x, y) {
+ this.x = x
+ this.y = y
+ }
+ _setWindowSize(windowSize) {
+ this._WINDOW_SIZE = windowSize
+ pointPrecomputes.delete(this)
+ }
+ static fromCompressedHex(bytes) {
+ const isShort = bytes.length === 32
+ const x = bytesToNumber(isShort ? bytes : bytes.subarray(1))
+ if (!isValidFieldElement(x)) throw new Error('Point is not on curve')
+ const y2 = weistrass(x)
+ let y = sqrtMod(y2)
+ const isYOdd = (y & _1n) === _1n
+ if (isShort) {
+ if (isYOdd) y = mod(-y)
+ } else {
+ const isFirstByteOdd = (bytes[0] & 1) === 1
+ if (isFirstByteOdd !== isYOdd) y = mod(-y)
+ }
+ const point = new Point(x, y)
+ point.assertValidity()
+ return point
+ }
+ static fromUncompressedHex(bytes) {
+ const x = bytesToNumber(bytes.subarray(1, 33))
+ const y = bytesToNumber(bytes.subarray(33, 65))
+ const point = new Point(x, y)
+ point.assertValidity()
+ return point
+ }
+ static fromHex(hex) {
+ const bytes = ensureBytes(hex)
+ const len = bytes.length
+ const header = bytes[0]
+ if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) {
+ return this.fromCompressedHex(bytes)
+ }
+ if (len === 65 && header === 0x04) return this.fromUncompressedHex(bytes)
+ throw new Error(
+ `Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`
+ )
+ }
+ static fromPrivateKey(privateKey) {
+ return Point.BASE.multiply(normalizePrivateKey(privateKey))
+ }
+ static fromSignature(msgHash, signature, recovery) {
+ msgHash = ensureBytes(msgHash)
+ const h = truncateHash(msgHash)
+ const {r, s} = normalizeSignature(signature)
+ if (recovery !== 0 && recovery !== 1) {
+ throw new Error('Cannot recover signature: invalid recovery bit')
+ }
+ const prefix = recovery & 1 ? '03' : '02'
+ const R = Point.fromHex(prefix + numTo32bStr(r))
+ const {n} = CURVE
+ const rinv = invert(r, n)
+ const u1 = mod(-h * rinv, n)
+ const u2 = mod(s * rinv, n)
+ const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2)
+ if (!Q) throw new Error('Cannot recover signature: point at infinify')
+ Q.assertValidity()
+ return Q
+ }
+ toRawBytes(isCompressed = false) {
+ return hexToBytes(this.toHex(isCompressed))
+ }
+ toHex(isCompressed = false) {
+ const x = numTo32bStr(this.x)
+ if (isCompressed) {
+ const prefix = this.y & _1n ? '03' : '02'
+ return `${prefix}${x}`
+ } else {
+ return `04${x}${numTo32bStr(this.y)}`
+ }
+ }
+ toHexX() {
+ return this.toHex(true).slice(2)
+ }
+ toRawX() {
+ return this.toRawBytes(true).slice(1)
+ }
+ assertValidity() {
+ const msg = 'Point is not on elliptic curve'
+ const {x, y} = this
+ if (!isValidFieldElement(x) || !isValidFieldElement(y))
+ throw new Error(msg)
+ const left = mod(y * y)
+ const right = weistrass(x)
+ if (mod(left - right) !== _0n) throw new Error(msg)
+ }
+ equals(other) {
+ return this.x === other.x && this.y === other.y
+ }
+ negate() {
+ return new Point(this.x, mod(-this.y))
+ }
+ double() {
+ return JacobianPoint.fromAffine(this).double().toAffine()
+ }
+ add(other) {
+ return JacobianPoint.fromAffine(this)
+ .add(JacobianPoint.fromAffine(other))
+ .toAffine()
+ }
+ subtract(other) {
+ return this.add(other.negate())
+ }
+ multiply(scalar) {
+ return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine()
+ }
+ multiplyAndAddUnsafe(Q, a, b) {
+ const P = JacobianPoint.fromAffine(this)
+ const aP =
+ a === _0n || a === _1n || this !== Point.BASE
+ ? P.multiplyUnsafe(a)
+ : P.multiply(a)
+ const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b)
+ const sum = aP.add(bQ)
+ return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine()
+ }
+ }
+ Point.BASE = new Point(CURVE.Gx, CURVE.Gy)
+ Point.ZERO = new Point(_0n, _0n)
+ function sliceDER(s) {
+ return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s
+ }
+ function parseDERInt(data) {
+ if (data.length < 2 || data[0] !== 0x02) {
+ throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`)
+ }
+ const len = data[1]
+ const res = data.subarray(2, len + 2)
+ if (!len || res.length !== len) {
+ throw new Error(`Invalid signature integer: wrong length`)
+ }
+ if (res[0] === 0x00 && res[1] <= 0x7f) {
+ throw new Error('Invalid signature integer: trailing length')
+ }
+ return {data: bytesToNumber(res), left: data.subarray(len + 2)}
+ }
+ function parseDERSignature(data) {
+ if (data.length < 2 || data[0] != 0x30) {
+ throw new Error(`Invalid signature tag: ${bytesToHex(data)}`)
+ }
+ if (data[1] !== data.length - 2) {
+ throw new Error('Invalid signature: incorrect length')
+ }
+ const {data: r, left: sBytes} = parseDERInt(data.subarray(2))
+ const {data: s, left: rBytesLeft} = parseDERInt(sBytes)
+ if (rBytesLeft.length) {
+ throw new Error(
+ `Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`
+ )
+ }
+ return {r, s}
+ }
+ class Signature {
+ constructor(r, s) {
+ this.r = r
+ this.s = s
+ this.assertValidity()
+ }
+ static fromCompact(hex) {
+ const arr = isUint8a(hex)
+ const name = 'Signature.fromCompact'
+ if (typeof hex !== 'string' && !arr)
+ throw new TypeError(`${name}: Expected string or Uint8Array`)
+ const str = arr ? bytesToHex(hex) : hex
+ if (str.length !== 128) throw new Error(`${name}: Expected 64-byte hex`)
+ return new Signature(
+ hexToNumber(str.slice(0, 64)),
+ hexToNumber(str.slice(64, 128))
+ )
+ }
+ static fromDER(hex) {
+ const arr = isUint8a(hex)
+ if (typeof hex !== 'string' && !arr)
+ throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`)
+ const {r, s} = parseDERSignature(arr ? hex : hexToBytes(hex))
+ return new Signature(r, s)
+ }
+ static fromHex(hex) {
+ return this.fromDER(hex)
+ }
+ assertValidity() {
+ const {r, s} = this
+ if (!isWithinCurveOrder(r))
+ throw new Error('Invalid Signature: r must be 0 < r < n')
+ if (!isWithinCurveOrder(s))
+ throw new Error('Invalid Signature: s must be 0 < s < n')
+ }
+ hasHighS() {
+ const HALF = CURVE.n >> _1n
+ return this.s > HALF
+ }
+ normalizeS() {
+ return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this
+ }
+ toDERRawBytes(isCompressed = false) {
+ return hexToBytes(this.toDERHex(isCompressed))
+ }
+ toDERHex(isCompressed = false) {
+ const sHex = sliceDER(numberToHexUnpadded(this.s))
+ if (isCompressed) return sHex
+ const rHex = sliceDER(numberToHexUnpadded(this.r))
+ const rLen = numberToHexUnpadded(rHex.length / 2)
+ const sLen = numberToHexUnpadded(sHex.length / 2)
+ const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4)
+ return `30${length}02${rLen}${rHex}02${sLen}${sHex}`
+ }
+ toRawBytes() {
+ return this.toDERRawBytes()
+ }
+ toHex() {
+ return this.toDERHex()
+ }
+ toCompactRawBytes() {
+ return hexToBytes(this.toCompactHex())
+ }
+ toCompactHex() {
+ return numTo32bStr(this.r) + numTo32bStr(this.s)
+ }
+ }
+ function concatBytes(...arrays) {
+ if (!arrays.every(isUint8a)) throw new Error('Uint8Array list expected')
+ if (arrays.length === 1) return arrays[0]
+ const length = arrays.reduce((a, arr) => a + arr.length, 0)
+ const result = new Uint8Array(length)
+ for (let i = 0, pad = 0; i < arrays.length; i++) {
+ const arr = arrays[i]
+ result.set(arr, pad)
+ pad += arr.length
+ }
+ return result
+ }
+ function isUint8a(bytes) {
+ return bytes instanceof Uint8Array
+ }
+ const hexes = Array.from({length: 256}, (v, i) =>
+ i.toString(16).padStart(2, '0')
+ )
+ function bytesToHex(uint8a) {
+ if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array')
+ let hex = ''
+ for (let i = 0; i < uint8a.length; i++) {
+ hex += hexes[uint8a[i]]
+ }
+ return hex
+ }
+ function numTo32bStr(num) {
+ if (num > POW_2_256) throw new Error('Expected number < 2^256')
+ return num.toString(16).padStart(64, '0')
+ }
+ function numTo32b(num) {
+ return hexToBytes(numTo32bStr(num))
+ }
+ function numberToHexUnpadded(num) {
+ const hex = num.toString(16)
+ return hex.length & 1 ? `0${hex}` : hex
+ }
+ function hexToNumber(hex) {
+ if (typeof hex !== 'string') {
+ throw new TypeError('hexToNumber: expected string, got ' + typeof hex)
+ }
+ return BigInt(`0x${hex}`)
+ }
+ function hexToBytes(hex) {
+ if (typeof hex !== 'string') {
+ throw new TypeError('hexToBytes: expected string, got ' + typeof hex)
+ }
+ if (hex.length % 2)
+ throw new Error('hexToBytes: received invalid unpadded hex' + hex.length)
+ const array = new Uint8Array(hex.length / 2)
+ for (let i = 0; i < array.length; i++) {
+ const j = i * 2
+ const hexByte = hex.slice(j, j + 2)
+ const byte = Number.parseInt(hexByte, 16)
+ if (Number.isNaN(byte) || byte < 0)
+ throw new Error('Invalid byte sequence')
+ array[i] = byte
+ }
+ return array
+ }
+ function bytesToNumber(bytes) {
+ return hexToNumber(bytesToHex(bytes))
+ }
+ function ensureBytes(hex) {
+ return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex)
+ }
+ function normalizeScalar(num) {
+ if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
+ return BigInt(num)
+ if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num
+ throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n')
+ }
+ function mod(a, b = CURVE.P) {
+ const result = a % b
+ return result >= _0n ? result : b + result
+ }
+ function pow2(x, power) {
+ const {P} = CURVE
+ let res = x
+ while (power-- > _0n) {
+ res *= res
+ res %= P
+ }
+ return res
+ }
+ function sqrtMod(x) {
+ const {P} = CURVE
+ const _6n = BigInt(6)
+ const _11n = BigInt(11)
+ const _22n = BigInt(22)
+ const _23n = BigInt(23)
+ const _44n = BigInt(44)
+ const _88n = BigInt(88)
+ const b2 = (x * x * x) % P
+ const b3 = (b2 * b2 * x) % P
+ const b6 = (pow2(b3, _3n) * b3) % P
+ const b9 = (pow2(b6, _3n) * b3) % P
+ const b11 = (pow2(b9, _2n) * b2) % P
+ const b22 = (pow2(b11, _11n) * b11) % P
+ const b44 = (pow2(b22, _22n) * b22) % P
+ const b88 = (pow2(b44, _44n) * b44) % P
+ const b176 = (pow2(b88, _88n) * b88) % P
+ const b220 = (pow2(b176, _44n) * b44) % P
+ const b223 = (pow2(b220, _3n) * b3) % P
+ const t1 = (pow2(b223, _23n) * b22) % P
+ const t2 = (pow2(t1, _6n) * b2) % P
+ return pow2(t2, _2n)
+ }
+ function invert(number, modulo = CURVE.P) {
+ if (number === _0n || modulo <= _0n) {
+ throw new Error(
+ `invert: expected positive integers, got n=${number} mod=${modulo}`
+ )
+ }
+ let a = mod(number, modulo)
+ let b = modulo
+ let x = _0n,
+ u = _1n
+ while (a !== _0n) {
+ const q = b / a
+ const r = b % a
+ const m = x - u * q
+ ;(b = a), (a = r), (x = u), (u = m)
+ }
+ const gcd = b
+ if (gcd !== _1n) throw new Error('invert: does not exist')
+ return mod(x, modulo)
+ }
+ function invertBatch(nums, p = CURVE.P) {
+ const scratch = new Array(nums.length)
+ const lastMultiplied = nums.reduce((acc, num, i) => {
+ if (num === _0n) return acc
+ scratch[i] = acc
+ return mod(acc * num, p)
+ }, _1n)
+ const inverted = invert(lastMultiplied, p)
+ nums.reduceRight((acc, num, i) => {
+ if (num === _0n) return acc
+ scratch[i] = mod(acc * scratch[i], p)
+ return mod(acc * num, p)
+ }, inverted)
+ return scratch
+ }
+ const divNearest = (a, b) => (a + b / _2n) / b
+ const POW_2_128 = _2n ** BigInt(128)
+ function splitScalarEndo(k) {
+ const {n} = CURVE
+ const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15')
+ const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3')
+ const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8')
+ const b2 = a1
+ const c1 = divNearest(b2 * k, n)
+ const c2 = divNearest(-b1 * k, n)
+ let k1 = mod(k - c1 * a1 - c2 * a2, n)
+ let k2 = mod(-c1 * b1 - c2 * b2, n)
+ const k1neg = k1 > POW_2_128
+ const k2neg = k2 > POW_2_128
+ if (k1neg) k1 = n - k1
+ if (k2neg) k2 = n - k2
+ if (k1 > POW_2_128 || k2 > POW_2_128) {
+ throw new Error('splitScalarEndo: Endomorphism failed, k=' + k)
+ }
+ return {k1neg, k1, k2neg, k2}
+ }
+ function truncateHash(hash) {
+ const {n} = CURVE
+ const byteLength = hash.length
+ const delta = byteLength * 8 - 256
+ let h = bytesToNumber(hash)
+ if (delta > 0) h = h >> BigInt(delta)
+ if (h >= n) h -= n
+ return h
+ }
+ class HmacDrbg {
+ constructor() {
+ this.v = new Uint8Array(32).fill(1)
+ this.k = new Uint8Array(32).fill(0)
+ this.counter = 0
+ }
+ hmac(...values) {
+ return utils.hmacSha256(this.k, ...values)
+ }
+ hmacSync(...values) {
+ if (typeof utils.hmacSha256Sync !== 'function')
+ throw new Error('utils.hmacSha256Sync is undefined, you need to set it')
+ const res = utils.hmacSha256Sync(this.k, ...values)
+ if (res instanceof Promise)
+ throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync')
+ return res
+ }
+ incr() {
+ if (this.counter >= 1000) {
+ throw new Error('Tried 1,000 k values for sign(), all were invalid')
+ }
+ this.counter += 1
+ }
+ async reseed(seed = new Uint8Array()) {
+ this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed)
+ this.v = await this.hmac(this.v)
+ if (seed.length === 0) return
+ this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed)
+ this.v = await this.hmac(this.v)
+ }
+ reseedSync(seed = new Uint8Array()) {
+ this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed)
+ this.v = this.hmacSync(this.v)
+ if (seed.length === 0) return
+ this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed)
+ this.v = this.hmacSync(this.v)
+ }
+ async generate() {
+ this.incr()
+ this.v = await this.hmac(this.v)
+ return this.v
+ }
+ generateSync() {
+ this.incr()
+ this.v = this.hmacSync(this.v)
+ return this.v
+ }
+ }
+ function isWithinCurveOrder(num) {
+ return _0n < num && num < CURVE.n
+ }
+ function isValidFieldElement(num) {
+ return _0n < num && num < CURVE.P
+ }
+ function kmdToSig(kBytes, m, d) {
+ const k = bytesToNumber(kBytes)
+ if (!isWithinCurveOrder(k)) return
+ const {n} = CURVE
+ const q = Point.BASE.multiply(k)
+ const r = mod(q.x, n)
+ if (r === _0n) return
+ const s = mod(invert(k, n) * mod(m + d * r, n), n)
+ if (s === _0n) return
+ const sig = new Signature(r, s)
+ const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n)
+ return {sig, recovery}
+ }
+ function normalizePrivateKey(key) {
+ let num
+ if (typeof key === 'bigint') {
+ num = key
+ } else if (
+ typeof key === 'number' &&
+ Number.isSafeInteger(key) &&
+ key > 0
+ ) {
+ num = BigInt(key)
+ } else if (typeof key === 'string') {
+ if (key.length !== 64) throw new Error('Expected 32 bytes of private key')
+ num = hexToNumber(key)
+ } else if (isUint8a(key)) {
+ if (key.length !== 32) throw new Error('Expected 32 bytes of private key')
+ num = bytesToNumber(key)
+ } else {
+ throw new TypeError('Expected valid private key')
+ }
+ if (!isWithinCurveOrder(num))
+ throw new Error('Expected private key: 0 < key < n')
+ return num
+ }
+ function normalizePublicKey(publicKey) {
+ if (publicKey instanceof Point) {
+ publicKey.assertValidity()
+ return publicKey
+ } else {
+ return Point.fromHex(publicKey)
+ }
+ }
+ function normalizeSignature(signature) {
+ if (signature instanceof Signature) {
+ signature.assertValidity()
+ return signature
+ }
+ try {
+ return Signature.fromDER(signature)
+ } catch (error) {
+ return Signature.fromCompact(signature)
+ }
+ }
+ function getPublicKey(privateKey, isCompressed = false) {
+ return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed)
+ }
+ function recoverPublicKey(
+ msgHash,
+ signature,
+ recovery,
+ isCompressed = false
+ ) {
+ return Point.fromSignature(msgHash, signature, recovery).toRawBytes(
+ isCompressed
+ )
+ }
+ function isPub(item) {
+ const arr = isUint8a(item)
+ const str = typeof item === 'string'
+ const len = (arr || str) && item.length
+ if (arr) return len === 33 || len === 65
+ if (str) return len === 66 || len === 130
+ if (item instanceof Point) return true
+ return false
+ }
+ function getSharedSecret(privateA, publicB, isCompressed = false) {
+ if (isPub(privateA))
+ throw new TypeError('getSharedSecret: first arg must be private key')
+ if (!isPub(publicB))
+ throw new TypeError('getSharedSecret: second arg must be public key')
+ const b = normalizePublicKey(publicB)
+ b.assertValidity()
+ return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed)
+ }
+ function bits2int(bytes) {
+ const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes
+ return bytesToNumber(slice)
+ }
+ function bits2octets(bytes) {
+ const z1 = bits2int(bytes)
+ const z2 = mod(z1, CURVE.n)
+ return int2octets(z2 < _0n ? z1 : z2)
+ }
+ function int2octets(num) {
+ if (typeof num !== 'bigint') throw new Error('Expected bigint')
+ const hex = numTo32bStr(num)
+ return hexToBytes(hex)
+ }
+ function initSigArgs(msgHash, privateKey, extraEntropy) {
+ if (msgHash == null)
+ throw new Error(`sign: expected valid message hash, not "${msgHash}"`)
+ const h1 = ensureBytes(msgHash)
+ const d = normalizePrivateKey(privateKey)
+ const seedArgs = [int2octets(d), bits2octets(h1)]
+ if (extraEntropy != null) {
+ if (extraEntropy === true) extraEntropy = utils.randomBytes(32)
+ const e = ensureBytes(extraEntropy)
+ if (e.length !== 32)
+ throw new Error('sign: Expected 32 bytes of extra data')
+ seedArgs.push(e)
+ }
+ const seed = concatBytes(...seedArgs)
+ const m = bits2int(h1)
+ return {seed, m, d}
+ }
+ function finalizeSig(recSig, opts) {
+ let {sig, recovery} = recSig
+ const {canonical, der, recovered} = Object.assign(
+ {canonical: true, der: true},
+ opts
+ )
+ if (canonical && sig.hasHighS()) {
+ sig = sig.normalizeS()
+ recovery ^= 1
+ }
+ const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes()
+ return recovered ? [hashed, recovery] : hashed
+ }
+ async function sign(msgHash, privKey, opts = {}) {
+ const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy)
+ let sig
+ const drbg = new HmacDrbg()
+ await drbg.reseed(seed)
+ while (!(sig = kmdToSig(await drbg.generate(), m, d))) await drbg.reseed()
+ return finalizeSig(sig, opts)
+ }
+ function signSync(msgHash, privKey, opts = {}) {
+ const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy)
+ let sig
+ const drbg = new HmacDrbg()
+ drbg.reseedSync(seed)
+ while (!(sig = kmdToSig(drbg.generateSync(), m, d))) drbg.reseedSync()
+ return finalizeSig(sig, opts)
+ }
+ const vopts = {strict: true}
+ function verify(signature, msgHash, publicKey, opts = vopts) {
+ let sig
+ try {
+ sig = normalizeSignature(signature)
+ msgHash = ensureBytes(msgHash)
+ } catch (error) {
+ return false
+ }
+ const {r, s} = sig
+ if (opts.strict && sig.hasHighS()) return false
+ const h = truncateHash(msgHash)
+ let P
+ try {
+ P = normalizePublicKey(publicKey)
+ } catch (error) {
+ return false
+ }
+ const {n} = CURVE
+ const sinv = invert(s, n)
+ const u1 = mod(h * sinv, n)
+ const u2 = mod(r * sinv, n)
+ const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)
+ if (!R) return false
+ const v = mod(R.x, n)
+ return v === r
+ }
+ function finalizeSchnorrChallenge(ch) {
+ return mod(bytesToNumber(ch), CURVE.n)
+ }
+ function hasEvenY(point) {
+ return (point.y & _1n) === _0n
+ }
+ class SchnorrSignature {
+ constructor(r, s) {
+ this.r = r
+ this.s = s
+ this.assertValidity()
+ }
+ static fromHex(hex) {
+ const bytes = ensureBytes(hex)
+ if (bytes.length !== 64)
+ throw new TypeError(
+ `SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`
+ )
+ const r = bytesToNumber(bytes.subarray(0, 32))
+ const s = bytesToNumber(bytes.subarray(32, 64))
+ return new SchnorrSignature(r, s)
+ }
+ assertValidity() {
+ const {r, s} = this
+ if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
+ throw new Error('Invalid signature')
+ }
+ toHex() {
+ return numTo32bStr(this.r) + numTo32bStr(this.s)
+ }
+ toRawBytes() {
+ return hexToBytes(this.toHex())
+ }
+ }
+ function schnorrGetPublicKey(privateKey) {
+ return Point.fromPrivateKey(privateKey).toRawX()
+ }
+ function initSchnorrSigArgs(message, privateKey, auxRand) {
+ if (message == null)
+ throw new TypeError(`sign: Expected valid message, not "${message}"`)
+ const m = ensureBytes(message)
+ const d0 = normalizePrivateKey(privateKey)
+ const rand = ensureBytes(auxRand)
+ if (rand.length !== 32)
+ throw new TypeError('sign: Expected 32 bytes of aux randomness')
+ const P = Point.fromPrivateKey(d0)
+ const px = P.toRawX()
+ const d = hasEvenY(P) ? d0 : CURVE.n - d0
+ return {m, P, px, d, rand}
+ }
+ function initSchnorrNonce(d, t0h) {
+ return numTo32b(d ^ bytesToNumber(t0h))
+ }
+ function finalizeSchnorrNonce(k0h) {
+ const k0 = mod(bytesToNumber(k0h), CURVE.n)
+ if (k0 === _0n)
+ throw new Error('sign: Creation of signature failed. k is zero')
+ const R = Point.fromPrivateKey(k0)
+ const rx = R.toRawX()
+ const k = hasEvenY(R) ? k0 : CURVE.n - k0
+ return {R, rx, k}
+ }
+ function finalizeSchnorrSig(R, k, e, d) {
+ return new SchnorrSignature(R.x, mod(k + e * d, CURVE.n)).toRawBytes()
+ }
+ async function schnorrSign(
+ message,
+ privateKey,
+ auxRand = utils.randomBytes()
+ ) {
+ const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand)
+ const t = initSchnorrNonce(d, await utils.taggedHash(TAGS.aux, rand))
+ const {R, rx, k} = finalizeSchnorrNonce(
+ await utils.taggedHash(TAGS.nonce, t, px, m)
+ )
+ const e = finalizeSchnorrChallenge(
+ await utils.taggedHash(TAGS.challenge, rx, px, m)
+ )
+ const sig = finalizeSchnorrSig(R, k, e, d)
+ const isValid = await schnorrVerify(sig, m, px)
+ if (!isValid) throw new Error('sign: Invalid signature produced')
+ return sig
+ }
+ function schnorrSignSync(message, privateKey, auxRand = utils.randomBytes()) {
+ const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand)
+ const t = initSchnorrNonce(d, utils.taggedHashSync(TAGS.aux, rand))
+ const {R, rx, k} = finalizeSchnorrNonce(
+ utils.taggedHashSync(TAGS.nonce, t, px, m)
+ )
+ const e = finalizeSchnorrChallenge(
+ utils.taggedHashSync(TAGS.challenge, rx, px, m)
+ )
+ const sig = finalizeSchnorrSig(R, k, e, d)
+ const isValid = schnorrVerifySync(sig, m, px)
+ if (!isValid) throw new Error('sign: Invalid signature produced')
+ return sig
+ }
+ function initSchnorrVerify(signature, message, publicKey) {
+ const raw = signature instanceof SchnorrSignature
+ const sig = raw ? signature : SchnorrSignature.fromHex(signature)
+ if (raw) sig.assertValidity()
+ return {
+ ...sig,
+ m: ensureBytes(message),
+ P: normalizePublicKey(publicKey)
+ }
+ }
+ function finalizeSchnorrVerify(r, P, s, e) {
+ const R = Point.BASE.multiplyAndAddUnsafe(
+ P,
+ normalizePrivateKey(s),
+ mod(-e, CURVE.n)
+ )
+ if (!R || !hasEvenY(R) || R.x !== r) return false
+ return true
+ }
+ async function schnorrVerify(signature, message, publicKey) {
+ try {
+ const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey)
+ const e = finalizeSchnorrChallenge(
+ await utils.taggedHash(TAGS.challenge, numTo32b(r), P.toRawX(), m)
+ )
+ return finalizeSchnorrVerify(r, P, s, e)
+ } catch (error) {
+ return false
+ }
+ }
+ function schnorrVerifySync(signature, message, publicKey) {
+ try {
+ const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey)
+ const e = finalizeSchnorrChallenge(
+ utils.taggedHashSync(TAGS.challenge, numTo32b(r), P.toRawX(), m)
+ )
+ return finalizeSchnorrVerify(r, P, s, e)
+ } catch (error) {
+ return false
+ }
+ }
+ const schnorr = {
+ Signature: SchnorrSignature,
+ getPublicKey: schnorrGetPublicKey,
+ sign: schnorrSign,
+ verify: schnorrVerify,
+ signSync: schnorrSignSync,
+ verifySync: schnorrVerifySync
+ }
+ Point.BASE._setWindowSize(8)
+ const crypto = {
+ node: nodeCrypto,
+ web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined
+ }
+ const TAGS = {
+ challenge: 'BIP0340/challenge',
+ aux: 'BIP0340/aux',
+ nonce: 'BIP0340/nonce'
+ }
+ const TAGGED_HASH_PREFIXES = {}
+ const utils = {
+ isValidPrivateKey(privateKey) {
+ try {
+ normalizePrivateKey(privateKey)
+ return true
+ } catch (error) {
+ return false
+ }
+ },
+ privateAdd: (privateKey, tweak) => {
+ const p = normalizePrivateKey(privateKey)
+ const t = normalizePrivateKey(tweak)
+ return numTo32b(mod(p + t, CURVE.n))
+ },
+ privateNegate: privateKey => {
+ const p = normalizePrivateKey(privateKey)
+ return numTo32b(CURVE.n - p)
+ },
+ pointAddScalar: (p, tweak, isCompressed) => {
+ const P = Point.fromHex(p)
+ const t = normalizePrivateKey(tweak)
+ const Q = Point.BASE.multiplyAndAddUnsafe(P, t, _1n)
+ if (!Q) throw new Error('Tweaked point at infinity')
+ return Q.toRawBytes(isCompressed)
+ },
+ pointMultiply: (p, tweak, isCompressed) => {
+ const P = Point.fromHex(p)
+ const t = bytesToNumber(ensureBytes(tweak))
+ return P.multiply(t).toRawBytes(isCompressed)
+ },
+ hashToPrivateKey: hash => {
+ hash = ensureBytes(hash)
+ if (hash.length < 40 || hash.length > 1024)
+ throw new Error('Expected 40-1024 bytes of private key as per FIPS 186')
+ const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n
+ return numTo32b(num)
+ },
+ randomBytes: (bytesLength = 32) => {
+ if (crypto.web) {
+ return crypto.web.getRandomValues(new Uint8Array(bytesLength))
+ } else if (crypto.node) {
+ const {randomBytes} = crypto.node
+ return Uint8Array.from(randomBytes(bytesLength))
+ } else {
+ throw new Error("The environment doesn't have randomBytes function")
+ }
+ },
+ randomPrivateKey: () => {
+ return utils.hashToPrivateKey(utils.randomBytes(40))
+ },
+ bytesToHex,
+ hexToBytes,
+ concatBytes,
+ mod,
+ invert,
+ sha256: async (...messages) => {
+ console.log('### sha256 messages', messages)
+ if (crypto.web) {
+ const buffer = await crypto.web.subtle.digest(
+ 'SHA-256',
+ concatBytes(...messages)
+ )
+ return new Uint8Array(buffer)
+ } else if (crypto.node) {
+ const {createHash} = crypto.node
+ const hash = createHash('sha256')
+ messages.forEach(m => hash.update(m))
+ return Uint8Array.from(hash.digest())
+ } else {
+ throw new Error("The environment doesn't have sha256 function")
+ }
+ },
+ hmacSha256: async (key, ...messages) => {
+ if (crypto.web) {
+ const ckey = await crypto.web.subtle.importKey(
+ 'raw',
+ key,
+ {name: 'HMAC', hash: {name: 'SHA-256'}},
+ false,
+ ['sign']
+ )
+ const message = concatBytes(...messages)
+ const buffer = await crypto.web.subtle.sign('HMAC', ckey, message)
+ return new Uint8Array(buffer)
+ } else if (crypto.node) {
+ const {createHmac} = crypto.node
+ const hash = createHmac('sha256', key)
+ messages.forEach(m => hash.update(m))
+ return Uint8Array.from(hash.digest())
+ } else {
+ throw new Error("The environment doesn't have hmac-sha256 function")
+ }
+ },
+ sha256Sync: undefined,
+ hmacSha256Sync: undefined,
+ taggedHash: async (tag, ...messages) => {
+ let tagP = TAGGED_HASH_PREFIXES[tag]
+ if (tagP === undefined) {
+ const tagH = await utils.sha256(
+ Uint8Array.from(tag, c => c.charCodeAt(0))
+ )
+ tagP = concatBytes(tagH, tagH)
+ TAGGED_HASH_PREFIXES[tag] = tagP
+ }
+ return utils.sha256(tagP, ...messages)
+ },
+ taggedHashSync: (tag, ...messages) => {
+ if (typeof utils.sha256Sync !== 'function')
+ throw new Error('utils.sha256Sync is undefined, you need to set it')
+ let tagP = TAGGED_HASH_PREFIXES[tag]
+ if (tagP === undefined) {
+ const tagH = utils.sha256Sync(
+ Uint8Array.from(tag, c => c.charCodeAt(0))
+ )
+ tagP = concatBytes(tagH, tagH)
+ TAGGED_HASH_PREFIXES[tag] = tagP
+ }
+ return utils.sha256Sync(tagP, ...messages)
+ },
+ precompute(windowSize = 8, point = Point.BASE) {
+ const cached = point === Point.BASE ? point : new Point(point.x, point.y)
+ cached._setWindowSize(windowSize)
+ cached.multiply(_3n)
+ return cached
+ }
+ }
+
+ exports.CURVE = CURVE
+ exports.Point = Point
+ exports.Signature = Signature
+ exports.getPublicKey = getPublicKey
+ exports.getSharedSecret = getSharedSecret
+ exports.recoverPublicKey = recoverPublicKey
+ exports.schnorr = schnorr
+ exports.sign = sign
+ exports.signSync = signSync
+ exports.utils = utils
+ exports.verify = verify
+
+ Object.defineProperty(exports, '__esModule', {value: true})
+})
diff --git a/lnbits/extensions/cashu/static/js/utils.js b/lnbits/extensions/cashu/static/js/utils.js
new file mode 100644
index 00000000..cf852b58
--- /dev/null
+++ b/lnbits/extensions/cashu/static/js/utils.js
@@ -0,0 +1,23 @@
+function splitAmount(value) {
+ const chunks = []
+ for (let i = 0; i < 32; i++) {
+ const mask = 1 << i
+ if ((value & mask) !== 0) chunks.push(Math.pow(2, i))
+ }
+ return chunks
+}
+
+function bytesToNumber(bytes) {
+ return hexToNumber(nobleSecp256k1.utils.bytesToHex(bytes))
+}
+
+function bigIntStringify(key, value) {
+ return typeof value === 'bigint' ? value.toString() : value
+}
+
+function hexToNumber(hex) {
+ if (typeof hex !== 'string') {
+ throw new TypeError('hexToNumber: expected string, got ' + typeof hex)
+ }
+ return BigInt(`0x${hex}`)
+}
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 8a25a8f7..eed6cb1d 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -81,6 +81,14 @@ page_container %}
Pending
+
+ Recheck
+
bt.hash = hash)
+ if (!tokens) {
+ console.error('####### no token for hash', hash)
+ return
+ }
+ const promises = await this.fetchPromisesFromMint(hash, tokens.blindedMessages)
+ if (promises && promises.length){
+ tokens.promises = promises
+ }
+ },
+
+ fetchPromisesFromMint: async function (hash, blindedMessages) {
+ console.log('### fetchPromisesFromMint', hash, blindedMessages)
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${hash}`,
+ '',
+ {
+ blinded_messages: blindedMessages
+ }
+ )
+ console.log('### fetchPromisesFromMint data', data)
+ return data
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
+
+ },
+
+ requestTokens: async function (amounts, paymentHash) {
+ const newTokens = await this.buildTokens(amounts, paymentHash)
+ this.tokens.push(newTokens)
+ localStorage.setItem(
+ 'cashu.tokens',
+ JSON.stringify(this.tokens, bigIntStringify)
+ )
+ console.log('### this.tokens', this.tokens)
+ await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
+ },
+
+ buildTokens: async function (amounts, paymentHash) {
+ const blindedMessages = []
+ const secrets = []
+ const randomBlindingFactors = []
+ for (let i = 0; i < amounts.length; i++) {
+ // const secret = bytesToNumber(nobleSecp256k1.utils.randomBytes(32)) + ''
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ secrets.push(secret)
+ const {B_, randomBlindingFactor} = await step1Bob(secret)
+ randomBlindingFactors.push(randomBlindingFactor)
+ blindedMessages.push({amount: amounts[i], B_: B_})
+ }
+
+ const newTokens = {
+ hash: paymentHash,
+ blindedMessages,
+ randomBlindingFactors,
+ secrets,
+ status: 'pending'
+ }
+ return newTokens
+ // console.log('### payloadsJson.payloads', payloadsJson.payloads)
+ // const promises = await mintApi.mint(payloadsJson.payloads, paymentHash)
+ // if (promises.error) {
+ // throw new Error(promises.error)
+ // }
+ // return this._constructProofs(promises, randomBlindingFactors, secrets)
+ },
+
+ _constructProofs: function (promises, randomBlindingFactors, secrets) {
+ return promises.map((p, i) => {
+ const C_ = nobleSecp256k1.Point.fromHex(p['C_'])
+ const A = this.keys[p.amount]
+ const C = step3Bob(
+ C_,
+ randomBlindingFactors[i],
+ nobleSecp256k1.Point.fromHex(A)
+ ).toHex()
+ return {
+ amount: p.amount,
+ C: {C, secret: secrets[i]}
+ }
+ })
}
},
watch: {
@@ -910,10 +1032,16 @@ page_container %}
this.tokenBuys = JSON.parse(
localStorage.getItem('cashu.tokenBuys') || '[]'
)
+ this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
console.log('#### this.tokenBuys', this.tokenBuys)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
+
+ this.checkXXXXXX()
}
})
+
+
+
{% endblock %}
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index e2410909..2f4b5ae4 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -285,10 +285,10 @@ async def mint_coins(
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."
- # )
+ if status.paid != True:
+ raise HTTPException(
+ status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
+ )
try:
await update_lightning_invoice(cashu_id, payment_hash, True)
From 13a46c1ebef96ad1ea6b2084bed9b685f0c48b4e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 13:06:11 +0300
Subject: [PATCH 0146/1058] chore: do not loose stuff
---
lnbits/extensions/cashu/static/js/dhke.js | 2 +-
lnbits/extensions/cashu/templates/cashu/wallet.html | 10 ++++++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index 73b1bcb8..959ba6f1 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -22,7 +22,7 @@ async function step1Bob(secretMessage) {
)
const P = nobleSecp256k1.Point.fromPrivateKey(randomBlindingFactor)
const B_ = Y.add(P)
- return {B_, randomBlindingFactor}
+ return {B_: B_.toHex(true), randomBlindingFactor}
}
function step3Bob(C_, r, A) {
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index eed6cb1d..e59be049 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -78,7 +78,7 @@ page_container %}
-
+
Pending
bt.hash = hash)
+ const tokens = this.tokens.find(bt => bt.hash = hash)
if (!tokens) {
console.error('####### no token for hash', hash)
return
From 4a046d5745b374de323bf6ece9dee6bf06f2794c Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 13:45:53 +0300
Subject: [PATCH 0147/1058] refactor: renamings
---
.../cashu/static/js/noble-secp256k1.js | 1 -
.../cashu/templates/cashu/wallet.html | 73 +++++++++----------
2 files changed, 34 insertions(+), 40 deletions(-)
diff --git a/lnbits/extensions/cashu/static/js/noble-secp256k1.js b/lnbits/extensions/cashu/static/js/noble-secp256k1.js
index 3b24b884..6a6bd441 100644
--- a/lnbits/extensions/cashu/static/js/noble-secp256k1.js
+++ b/lnbits/extensions/cashu/static/js/noble-secp256k1.js
@@ -1092,7 +1092,6 @@
mod,
invert,
sha256: async (...messages) => {
- console.log('### sha256 messages', messages)
if (crypto.web) {
const buffer = await crypto.web.subtle.digest(
'SHA-256',
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index e59be049..ee052fc9 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -67,7 +67,7 @@ page_container %}
-
+
{% raw %}
-
+
Copy invoice
Date: Mon, 10 Oct 2022 13:54:52 +0300
Subject: [PATCH 0148/1058] refactor: rename
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index ee052fc9..6933acfd 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -85,7 +85,7 @@ page_container %}
size="lg"
color="secondary"
class="q-mr-md cursor-pointer"
- @click="recheckToken(props.row.hash)"
+ @click="recheckBuyOrder(props.row.hash)"
>
Recheck
@@ -893,8 +893,8 @@ page_container %}
}
},
- recheckToken: async function(hash) {
- console.log('### recheckToken', hash)
+ recheckBuyOrder: async function(hash) {
+ console.log('### recheckBuyOrder', hash)
const tokens = this.tokens.find(bt => bt.hash = hash)
if (!tokens) {
console.error('####### no token for hash', hash)
From 07eea856777321b6c52a464d3d1871f41a48ee25 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 13:58:20 +0300
Subject: [PATCH 0149/1058] refactor: extract store functions
---
.../cashu/templates/cashu/wallet.html | 47 +++++++++++--------
1 file changed, 28 insertions(+), 19 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 6933acfd..9a29ed79 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -78,7 +78,11 @@ page_container %}
-
+
Pending
- Recheck
+ Recheck
@@ -492,9 +496,9 @@ page_container %}
buyOrders: [],
buyData: {
amount: 0,
- memo: '',
- bolt11: '',
- hash: '',
+ memo: '',
+ bolt11: '',
+ hash: ''
},
showInvoiceDetails: false,
tokens: [],
@@ -843,7 +847,6 @@ page_container %}
this.buyData = data
this.showInvoiceDetails = true
},
-
requestInvoice: async function () {
try {
@@ -860,10 +863,7 @@ page_container %}
date: currentDateStr(),
status: 'pending'
})
- localStorage.setItem(
- 'cashu.buyOrders',
- JSON.stringify(this.buyOrders)
- )
+ this.storeBuyOrders()
const amounts = splitAmount(this.buyData.amount)
await this.requestTokens(amounts, this.buyData.hash)
} catch (error) {
@@ -893,15 +893,18 @@ page_container %}
}
},
- recheckBuyOrder: async function(hash) {
+ recheckBuyOrder: async function (hash) {
console.log('### recheckBuyOrder', hash)
- const tokens = this.tokens.find(bt => bt.hash = hash)
+ const tokens = this.tokens.find(bt => (bt.hash = hash))
if (!tokens) {
console.error('####### no token for hash', hash)
return
}
- const promises = await this.fetchPromisesFromMint(hash, tokens.blindedMessages)
- if (promises && promises.length){
+ const promises = await this.fetchPromisesFromMint(
+ hash,
+ tokens.blindedMessages
+ )
+ if (promises && promises.length) {
tokens.promises = promises
}
},
@@ -923,16 +926,12 @@ page_container %}
console.error(error)
LNbits.utils.notifyApiError(error)
}
-
},
requestTokens: async function (amounts, paymentHash) {
const newTokens = await this.buildTokens(amounts, paymentHash)
this.tokens.push(newTokens)
- localStorage.setItem(
- 'cashu.tokens',
- JSON.stringify(this.tokens, bigIntStringify)
- )
+ this.storeTokens()
console.log('### this.tokens', this.tokens)
await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
},
@@ -980,6 +979,16 @@ page_container %}
C: {C, secret: secrets[i]}
}
})
+ },
+
+ storeBuyOrders: function () {
+ localStorage.setItem('cashu.buyOrders', JSON.stringify(this.buyOrders))
+ },
+ storeTokens: function () {
+ localStorage.setItem(
+ 'cashu.tokens',
+ JSON.stringify(this.tokens, bigIntStringify)
+ )
}
},
watch: {
From 0a7b0819bbb5bac210d7000aef4a15fcebbe55d8 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 10 Oct 2022 12:17:35 +0100
Subject: [PATCH 0150/1058] get saved data and alert when data changed
---
.../admin/templates/admin/index.html | 18 +++++++-----------
lnbits/extensions/admin/views_api.py | 5 +++--
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 4754656d..4e401cb4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -3,7 +3,7 @@
-
+
{
+ this.settings = response.data.settings
+ this.formData = _.clone(this.settings)
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c8120564..c2079e37 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -43,8 +43,9 @@ async def api_update_settings(
user: User = Depends(check_admin),
data: UpdateSettings = Body(...),
):
- await update_settings(data)
- return {"status": "Success"}
+ settings = await update_settings(data)
+ logger.debug(settings)
+ return {"status": "Success", "settings": settings.dict()}
@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK)
From 7f20755c5ab681737b7aa6999ec4e33358701783 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 14:22:00 +0300
Subject: [PATCH 0151/1058] chore: here and there
---
.../cashu/templates/cashu/wallet.html | 24 +++++++++++++++----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 9a29ed79..c9f1b555 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -94,6 +94,12 @@ page_container %}
Recheck
+
+
+
(bt.hash = hash))
+ console.log('### recheckBuyOrder.hash', hash)
+ const tokens = this.tokens.find(bt => (bt.hash === hash))
+ console.log('### recheckBuyOrder.tokens', tokens)
if (!tokens) {
console.error('####### no token for hash', hash)
return
@@ -906,6 +913,12 @@ page_container %}
)
if (promises && promises.length) {
tokens.promises = promises
+ tokens.status = 'paid'
+ this.storeTokens()
+
+ const buyOrder = this.buyOrders.find(bo => bo.hash === hash)
+ buyOrder.status = 'paid'
+ this.storeBuyOrders()
}
},
@@ -1043,7 +1056,8 @@ page_container %}
localStorage.getItem('cashu.buyOrders') || '[]'
)
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
- console.log('#### this.buyOrders', this.buyOrders)
+ console.table(this.buyOrders)
+ console.table(this.tokens)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
From a3b05e26b718db4969884e642416b2eac119f506 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 10 Oct 2022 12:23:19 +0100
Subject: [PATCH 0152/1058] cleanup and typing fix for data
---
lnbits/extensions/admin/models.py | 12 +++++-----
.../admin/templates/admin/index.html | 22 ++-----------------
lnbits/extensions/admin/views_api.py | 1 -
3 files changed, 9 insertions(+), 26 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 45cd990d..94fa56bb 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,13 +1,15 @@
+from typing import List
+
from fastapi import Query
from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: str = Query(None) #this should be List[str] ??
- lnbits_allowed_users: str = Query(None) #this should be List[str] ??
- lnbits_admin_ext: str = Query(None) #this should be List[str] ??
- lnbits_disabled_ext: str = Query(None) #this should be List[str] ??
+ lnbits_admin_users: List[str] = Query(None)
+ lnbits_allowed_users: List[str] = Query(None)
+ lnbits_admin_ext: List[str] = Query(None)
+ lnbits_disabled_ext: List[str] = Query(None)
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -21,4 +23,4 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: str = Query(None) #this should be List[str] ??
+ lnbits_ad_space: List[str] = Query(None)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 4e401cb4..d8111595 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -209,26 +209,8 @@
})
},
updateSettings() {
- let data = {
- lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class,
- lnbits_admin_users: this.formData.lnbits_admin_users.toString(),
- lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(),
- lnbits_admin_ext: this.formData.lnbits_admin_ext,
- lnbits_disabled_ext: this.formData.lnbits_disabled_ext,
- lnbits_funding_source: this.formData.lnbits_funding_source,
- lnbits_force_https: this.formData.lnbits_force_https,
- lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min,
- lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent,
- lnbits_service_fee: this.formData.lnbits_service_fee,
- lnbits_hide_api: this.formData.lnbits_hide_api,
- lnbits_site_title: this.formData.lnbits_site_title,
- lnbits_site_tagline: this.formData.lnbits_site_tagline,
- lnbits_site_description: this.formData.lnbits_site_description,
- lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name,
- lnbits_denomination: this.formData.lnbits_denomination,
- lnbits_theme: this.formData.lnbits_theme,
- lnbits_custom_logo: this.formData.lnbits_custom_logo,
- lnbits_ad_space: this.formData.lnbits_ad_space.toString()
+ let data = {
+ ...this.formData
}
LNbits.api
.request(
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c2079e37..19b52e35 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -44,7 +44,6 @@ async def api_update_settings(
data: UpdateSettings = Body(...),
):
settings = await update_settings(data)
- logger.debug(settings)
return {"status": "Success", "settings": settings.dict()}
From 9fd6a26ec54d19145174acf7cec453ca9ef211c3 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 14:25:25 +0300
Subject: [PATCH 0153/1058] chore: code clean-up
---
.../cashu/templates/cashu/wallet.html | 234 +-----------------
1 file changed, 2 insertions(+), 232 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index c9f1b555..01a5bc1a 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -126,234 +126,6 @@ page_container %}
-
- {% raw %}
-
-
-
- {{receive.lnurl.domain}} is requesting an invoice:
-
- {% endraw %} {% if LNBITS_DENOMINATION != 'sats' %}
-
- {% else %}
-
-
- {% endif %}
-
-
- {% raw %}
-
-
-
- Withdraw from {{receive.lnurl.domain}}
-
- Create invoice
-
- Cancel
-
-
-
-
-
-
-
- Copy invoice
- Close
-
-
- {% endraw %}
-
-
-
-
-
-
- {% raw %} {{ parseFloat(String(parse.invoice.fsat).replaceAll(",",
- "")) / 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
-
-
- {{ parse.invoice.fsat }}{% endraw %} {{LNBITS_DENOMINATION}} {%
- raw %}
-
-
-
- Description: {{ parse.invoice.description }}
- Expire date: {{ parse.invoice.expireDate }}
- Hash: {{ parse.invoice.hash }}
-
- {% endraw %}
-
- Pay
- Cancel
-
-
- Not enough funds!
- Cancel
-
-
-
- {% raw %}
-
-
- Authenticate with {{ parse.lnurlauth.domain }} ?
-
-
-
- For every website and for every LNbits wallet, a new keypair
- will be deterministically generated so your identity can't be
- tied to your LNbits wallet or linked across websites. No other
- data will be shared with {{ parse.lnurlauth.domain }}.
-
- Your public key for {{ parse.lnurlauth.domain }} is:
-
- {{ parse.lnurlauth.pubkey }}
-
-
- Login
- Cancel
-
-
- {% endraw %}
-
-
-
-
-
-
- Read
- Cancel
-
-
-
-
-
-
-
-
-
-
-
-
-
- Cancel
-
-
-
-
-
-
-
-
-
-
-
- {% raw %}
@@ -445,7 +216,6 @@ page_container %}
>
- {% endraw %}
@@ -1056,8 +826,8 @@ page_container %}
localStorage.getItem('cashu.buyOrders') || '[]'
)
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
- console.table(this.buyOrders)
- console.table(this.tokens)
+ // console.table(this.buyOrders)
+ // console.table(this.tokens)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
From 8af4f95cc1fa70be6d606ce0612d46fc08537204 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 15:05:32 +0300
Subject: [PATCH 0154/1058] chore: parse invoice
---
.../cashu/templates/cashu/wallet.html | 127 ++++++++++++++++--
1 file changed, 117 insertions(+), 10 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 01a5bc1a..b3ccbc8b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -28,7 +28,12 @@ page_container %}
- Sell tokens
(for sats)
@@ -95,10 +100,7 @@ page_container %}
-
+
-
@@ -217,6 +218,51 @@ page_container %}
+
+
+
+
+
+
+ Please paste a Lightning invoice
+
+
+
+
+
+ {% raw %}
+ Amount: {{ sellData.invoice.sat }} sats
+ Description: {{ sellData.invoice.description }}
+ Expire date: {{ sellData.invoice.expireDate }}
+ Expired: {{ sellData.invoice.expired }}
+ Hash: {{ sellData.invoice.hash }} {% endraw %}
+
+
+ Check Invoice
+ Sell Token
+ Close
+
+
+
@@ -276,7 +322,12 @@ page_container %}
bolt11: '',
hash: ''
},
+ sellData: {
+ invoice: '',
+ bolt11: ''
+ },
showInvoiceDetails: false,
+ showPayInvoice: false,
tokens: [],
receive: {
@@ -619,11 +670,18 @@ page_container %}
this.showInvoiceDetails = true
},
- showInvoiceDialog: async function (data) {
+ showInvoiceDialog: function (data) {
this.buyData = _.clone(data)
this.showInvoiceDetails = true
},
+ showPayInvoiceDialog: function () {
+ console.log('### showPayInvoiceDialog')
+ this.sellData.invoice = ''
+ this.sellData.bolt11 = ''
+ this.showPayInvoice = true
+ },
+
requestInvoice: async function () {
try {
const {data} = await LNbits.api.request(
@@ -671,7 +729,7 @@ page_container %}
recheckBuyOrder: async function (hash) {
console.log('### recheckBuyOrder.hash', hash)
- const tokens = this.tokens.find(bt => (bt.hash === hash))
+ const tokens = this.tokens.find(bt => bt.hash === hash)
console.log('### recheckBuyOrder.tokens', tokens)
if (!tokens) {
console.error('####### no token for hash', hash)
@@ -764,6 +822,55 @@ page_container %}
})
},
+ checkInvoice: function () {
+ console.log('#### checkInvoice')
+ try {
+ const invoice = decode(this.sellData.bolt11)
+
+ const cleanInvoice = {
+ msat: invoice.human_readable_part.amount,
+ sat: invoice.human_readable_part.amount / 1000,
+ fsat: LNbits.utils.formatSat(
+ invoice.human_readable_part.amount / 1000
+ )
+ }
+
+ _.each(invoice.data.tags, tag => {
+ if (_.isObject(tag) && _.has(tag, 'description')) {
+ if (tag.description === 'payment_hash') {
+ cleanInvoice.hash = tag.value
+ } else if (tag.description === 'description') {
+ cleanInvoice.description = tag.value
+ } else if (tag.description === 'expiry') {
+ var expireDate = new Date(
+ (invoice.data.time_stamp + tag.value) * 1000
+ )
+ cleanInvoice.expireDate = Quasar.utils.date.formatDate(
+ expireDate,
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ'
+ )
+ cleanInvoice.expired = false // TODO
+ }
+ }
+
+ this.sellData.invoice = cleanInvoice
+ })
+
+ console.log('#### this.sellData.invoice', this.sellData.invoice)
+ } catch (error) {
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: 'Cannot decode invoice',
+ caption: error + ''
+ })
+ }
+ },
+
+ sellTokens: async function () {
+ console.log('#### sell tokens')
+ },
+
storeBuyOrders: function () {
localStorage.setItem('cashu.buyOrders', JSON.stringify(this.buyOrders))
},
@@ -826,8 +933,8 @@ page_container %}
localStorage.getItem('cashu.buyOrders') || '[]'
)
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
- // console.table(this.buyOrders)
- // console.table(this.tokens)
+ console.log('### buyOrders',this.buyOrders)
+ console.table('### tokens',this.tokens)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
From 422711649fb2079708c616770966ccbfd90fc50e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 22:56:03 +0300
Subject: [PATCH 0155/1058] feat: secret stuff
---
lnbits/extensions/cashu/core/b_dhke.py | 37 +++----
lnbits/extensions/cashu/mint_helper.py | 9 +-
lnbits/extensions/cashu/static/js/base64.js | 40 +++++++
lnbits/extensions/cashu/static/js/dhke.js | 19 ++--
.../cashu/templates/cashu/wallet.html | 100 +++++++++++++-----
5 files changed, 146 insertions(+), 59 deletions(-)
create mode 100644 lnbits/extensions/cashu/static/js/base64.js
diff --git a/lnbits/extensions/cashu/core/b_dhke.py b/lnbits/extensions/cashu/core/b_dhke.py
index be9a141b..8855481c 100644
--- a/lnbits/extensions/cashu/core/b_dhke.py
+++ b/lnbits/extensions/cashu/core/b_dhke.py
@@ -6,7 +6,7 @@ Alice:
A = a*G
return A
Bob:
-Y = hash_to_point(secret_message)
+Y = hash_to_curve(secret_message)
r = random blinding factor
B'= Y + r*G
return B'
@@ -20,7 +20,7 @@ C = C' - r*A
(= a*Y)
return C, secret_message
Alice:
-Y = hash_to_point(secret_message)
+Y = hash_to_curve(secret_message)
C == a*Y
If true, C must have originated from Alice
"""
@@ -30,28 +30,23 @@ import hashlib
from secp256k1 import PrivateKey, PublicKey
-def hash_to_point(secret_msg):
- """Generates x coordinate from the message hash and checks if the point lies on the curve.
- If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
- point = None
- msg = secret_msg
- while point is None:
- _hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
- try:
- # We construct compressed pub which has x coordinate encoded with even y
- _hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
- _hash[0] = 0x02 # set first byte to represent even y coord
- _hash = bytes(_hash)
- point = PublicKey(_hash, raw=True)
- except:
- msg = _hash
-
+def hash_to_curve(message: bytes):
+ """Generates a point from the message hash and checks if the point lies on the curve.
+ If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
+ point = None
+ msg_to_hash = message
+ while point is None:
+ try:
+ _hash = hashlib.sha256(msg_to_hash).digest()
+ point = PublicKey(b"\x02" + _hash, raw=True)
+ except:
+ msg_to_hash = _hash
return point
def step1_alice(secret_msg):
- secret_msg = secret_msg.encode("utf-8")
- Y = hash_to_point(secret_msg)
+ secret_msg = secret_msg
+ Y = hash_to_curve(secret_msg)
r = PrivateKey()
B_ = Y + r.pubkey
return B_, r
@@ -68,7 +63,7 @@ def step3_alice(C_, r, A):
def verify(a, C, secret_msg):
- Y = hash_to_point(secret_msg.encode("utf-8"))
+ Y = hash_to_curve(secret_msg)
return C == Y.mult(a)
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 5c96d831..3892c67f 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -1,4 +1,5 @@
import hashlib
+import base64
from typing import List, Set
from .core.b_dhke import verify
@@ -32,14 +33,16 @@ def derive_pubkeys(keys: List[PrivateKey]):
# async required?
async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
"""Verifies that the proof of promise was issued by this ledger."""
- if proof.secret in proofs_used:
- raise Exception(f"tokens already spent. Secret: {proof.secret}")
+ # if proof.secret in proofs_used:
+ # raise Exception(f"tokens already spent. Secret: {proof.secret}")
secret_key = derive_keys(master_prvkey)[
proof.amount
] # Get the correct key to check against
C = PublicKey(bytes.fromhex(proof.C), raw=True)
- validMintSig = verify(secret_key, C, proof.secret)
+ secret = base64.urlsafe_b64decode(proof.secret)
+ print('### secret', secret)
+ validMintSig = verify(secret_key, C, secret)
if validMintSig != True:
raise Exception(f"tokens not valid. Secret: {proof.secret}")
diff --git a/lnbits/extensions/cashu/static/js/base64.js b/lnbits/extensions/cashu/static/js/base64.js
new file mode 100644
index 00000000..2cd19a83
--- /dev/null
+++ b/lnbits/extensions/cashu/static/js/base64.js
@@ -0,0 +1,40 @@
+function unescapeBase64Url (str) {
+ return (str + '==='.slice((str.length + 3) % 4))
+ .replace(/-/g, '+')
+ .replace(/_/g, '/')
+ }
+
+ function escapeBase64Url (str) {
+ return str.replace(/\+/g, '-')
+ .replace(/\//g, '_')
+ .replace(/=/g, '')
+ }
+
+ const uint8ToBase64 = (function (exports) {
+ 'use strict';
+
+ var fromCharCode = String.fromCharCode;
+ var encode = function encode(uint8array) {
+ var output = [];
+
+ for (var i = 0, length = uint8array.length; i < length; i++) {
+ output.push(fromCharCode(uint8array[i]));
+ }
+
+ return btoa(output.join(''));
+ };
+
+ var asCharCode = function asCharCode(c) {
+ return c.charCodeAt(0);
+ };
+
+ var decode = function decode(chars) {
+ return Uint8Array.from(atob(chars), asCharCode);
+ };
+
+ exports.decode = decode;
+ exports.encode = encode;
+
+ return exports;
+
+ }({}));
\ No newline at end of file
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index 959ba6f1..c9e2d146 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -1,15 +1,19 @@
async function hashToCurve(secretMessage) {
+ console.log(
+ '### secretMessage',
+ nobleSecp256k1.utils.bytesToHex(secretMessage)
+ )
let point
while (!point) {
const hash = await nobleSecp256k1.utils.sha256(secretMessage)
+ const hashHex = nobleSecp256k1.utils.bytesToHex(hash)
+ const pointX = '02' + hashHex
+ console.log('### pointX', pointX)
try {
- point = nobleSecp256k1.Point.fromHex(hash)
+ point = nobleSecp256k1.Point.fromHex(pointX)
+ console.log('### point', point.toHex())
} catch (error) {
- // console.error(error)
- // const x = bytesToNumber(hash) + ''
- // const msg = await nobleSecp256k1.utils.sha256(x)
- secretMessage = await nobleSecp256k1.utils.sha256(hash)
- // secretMessage = nobleSecp256k1.utils.bytesToHex(msg)
+ secretMessage = await nobleSecp256k1.utils.sha256(secretMessage)
}
}
return point
@@ -26,6 +30,7 @@ async function step1Bob(secretMessage) {
}
function step3Bob(C_, r, A) {
- const C = C_.subtract(A.multiply(r))
+ const rInt = BigInt(r)
+ const C = C_.subtract(A.multiply(rInt))
return C
}
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index b3ccbc8b..cef80d42 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -240,7 +240,8 @@ page_container %}
{% raw %}
- Amount: {{ sellData.invoice.sat }} sats
+ Amount: {{ sellData.invoice.sat }}
+ sats
Description: {{ sellData.invoice.description }}
Expire date: {{ sellData.invoice.expireDate }}
Expired: {{ sellData.invoice.expired }}
@@ -315,6 +316,7 @@ page_container %}
mintId: '',
mintName: '',
+ keys: '',
buyOrders: [],
buyData: {
amount: 0,
@@ -782,9 +784,14 @@ page_container %}
const secrets = []
const randomBlindingFactors = []
for (let i = 0; i < amounts.length; i++) {
- // const secret = bytesToNumber(nobleSecp256k1.utils.randomBytes(32)) + ''
- const secret = nobleSecp256k1.utils.randomBytes(32)
- secrets.push(secret)
+ // const secret = nobleSecp256k1.utils.randomBytes(32)
+ const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
+ const encodedSecret = uint8ToBase64.encode(secret)
+ console.log('### encodedSecret', encodedSecret)
+ const decodedSecret = uint8ToBase64.decode(encodedSecret)
+ const hexSecret = nobleSecp256k1.utils.bytesToHex(decodedSecret)
+ console.log('### decodedSecret', hexSecret)
+ secrets.push(encodedSecret)
const {B_, randomBlindingFactor} = await step1Bob(secret)
randomBlindingFactors.push(randomBlindingFactor)
blindedMessages.push({amount: amounts[i], B_: B_})
@@ -798,28 +805,6 @@ page_container %}
status: 'pending'
}
return newTokens
- // console.log('### payloadsJson.payloads', payloadsJson.payloads)
- // const promises = await mintApi.mint(payloadsJson.payloads, paymentHash)
- // if (promises.error) {
- // throw new Error(promises.error)
- // }
- // return this._constructProofs(promises, randomBlindingFactors, secrets)
- },
-
- _constructProofs: function (promises, randomBlindingFactors, secrets) {
- return promises.map((p, i) => {
- const C_ = nobleSecp256k1.Point.fromHex(p['C_'])
- const A = this.keys[p.amount]
- const C = step3Bob(
- C_,
- randomBlindingFactors[i],
- nobleSecp256k1.Point.fromHex(A)
- ).toHex()
- return {
- amount: p.amount,
- C: {C, secret: secrets[i]}
- }
- })
},
checkInvoice: function () {
@@ -869,6 +854,57 @@ page_container %}
sellTokens: async function () {
console.log('#### sell tokens')
+ const amount = this.sellData.invoice.sat
+ const token = this.tokens
+ .filter(t => t.promises?.length)
+ .find(t => t.promises.find(b => b.amount === amount))
+ console.log('### token', token)
+ if (token) {
+ const promiseIndex = token.promises
+ .map(p => `${p.amount}`)
+ .indexOf(`${amount}`)
+ const promise = token.promises[promiseIndex]
+ console.log('### promise', promise)
+
+ const secret = token.secrets[promiseIndex]
+ const randomBlindingFactor = token.randomBlindingFactors[promiseIndex]
+
+ const C_ = nobleSecp256k1.Point.fromHex(promise['C_'])
+ const A = this.keys[promise.amount] // todo
+
+ console.log('#### C_', C_)
+ console.log('#### A', A)
+
+ const C = step3Bob(
+ C_,
+ randomBlindingFactor,
+ nobleSecp256k1.Point.fromHex(A)
+ )
+
+ const proofs = [
+ {
+ amount,
+ secret,
+ C: C.toHex(true)
+ }
+ ]
+
+ const payload = {
+ proofs,
+ amount,
+ invoice: this.sellData.bolt11
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ }
+ },
+
+ fetchMintKeys: async function () {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/cashu/${this.mintId}/keys`
+ )
+ this.keys = data
+ localStorage.setItem('cashu.keys', JSON.stringify(data))
},
storeBuyOrders: function () {
@@ -929,12 +965,19 @@ page_container %}
this.mintName = this.$q.localStorage.getItem('cashu.name')
}
+ const keysJson = localStorage.getItem('cashu.keys')
+ if (!keysJson) {
+ this.fetchMintKeys()
+ } else {
+ this.keys = JSON.parse(keysJson)
+ }
+
this.buyOrders = JSON.parse(
localStorage.getItem('cashu.buyOrders') || '[]'
)
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
- console.log('### buyOrders',this.buyOrders)
- console.table('### tokens',this.tokens)
+ console.log('### buyOrders', this.buyOrders)
+ console.table('### tokens', this.tokens)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
@@ -945,4 +988,5 @@ page_container %}
+
{% endblock %}
From 5f435125943144d6b4f3984b8815c6ead115a2f1 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Mon, 10 Oct 2022 23:06:16 +0300
Subject: [PATCH 0156/1058] chore: remove mock data
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index cef80d42..0d250a62 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -784,13 +784,10 @@ page_container %}
const secrets = []
const randomBlindingFactors = []
for (let i = 0; i < amounts.length; i++) {
- // const secret = nobleSecp256k1.utils.randomBytes(32)
- const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
+ // todo: base64Url
const encodedSecret = uint8ToBase64.encode(secret)
- console.log('### encodedSecret', encodedSecret)
- const decodedSecret = uint8ToBase64.decode(encodedSecret)
- const hexSecret = nobleSecp256k1.utils.bytesToHex(decodedSecret)
- console.log('### decodedSecret', hexSecret)
secrets.push(encodedSecret)
const {B_, randomBlindingFactor} = await step1Bob(secret)
randomBlindingFactors.push(randomBlindingFactor)
From 91a5f7d2143ac4e2eb4f58607a062c1386bea6e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 10 Oct 2022 23:27:46 +0200
Subject: [PATCH 0157/1058] add callback for saas app
---
lnbits/app.py | 2 +-
lnbits/extensions/admin/migrations.py | 3 +++
lnbits/extensions/admin/models.py | 10 ++++-----
lnbits/extensions/admin/views_api.py | 5 -----
lnbits/settings.py | 32 +++++++++++++++++++++++----
5 files changed, 37 insertions(+), 15 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index a8371950..6fa5cf5d 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -64,7 +64,7 @@ def create_app() -> FastAPI:
# TODO: why those 2?
g().config = settings
- # g().base_url = f"http://{settings.host}:{settings.port}"
+ g().base_url = f"http://{settings.host}:{settings.port}"
app.add_middleware(GZipMiddleware, minimum_size=1000)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index c4bc98d8..ea698c27 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -6,6 +6,9 @@ async def m001_create_admin_settings_table(db):
debug TEXT,
host TEXT,
port INTEGER,
+ lnbits_saas_instance_id TEXT,
+ lnbits_saas_callback TEXT,
+ lnbits_saas_secret TEXT,
lnbits_path TEXT,
lnbits_commit TEXT,
lnbits_admin_users TEXT,
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 94fa56bb..ada84e9d 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -6,10 +6,10 @@ from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: List[str] = Query(None)
- lnbits_allowed_users: List[str] = Query(None)
- lnbits_admin_ext: List[str] = Query(None)
- lnbits_disabled_ext: List[str] = Query(None)
+ lnbits_admin_users: List[str] = Query(None)
+ lnbits_allowed_users: List[str] = Query(None)
+ lnbits_admin_ext: List[str] = Query(None)
+ lnbits_disabled_ext: List[str] = Query(None)
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -23,4 +23,4 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: List[str] = Query(None)
+ lnbits_ad_space: List[str] = Query(None)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 19b52e35..ae2959bc 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -53,8 +53,3 @@ async def api_delete_settings(
):
await delete_settings()
return {"status": "Success"}
-
-
-@admin_ext.get("/api/v1/backup/", status_code=HTTPStatus.OK)
-async def api_backup(user: User = Depends(check_admin)):
- return {"status": "not implemented"}
diff --git a/lnbits/settings.py b/lnbits/settings.py
index ffcdcc0a..f183211c 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,6 +1,7 @@
import importlib
import json
import subprocess
+import httpx
from os import path
from sqlite3 import Row
from typing import List, Optional
@@ -9,6 +10,7 @@ from loguru import logger
from pydantic import BaseSettings, Field, validator
+
def list_parse_fallback(v):
try:
return json.loads(v)
@@ -34,6 +36,11 @@ class Settings(BaseSettings):
lnbits_path: str = Field(default=".")
lnbits_commit: str = Field(default="unknown")
+ # saas
+ lnbits_saas_callback: Optional[str] = Field(default=None)
+ lnbits_saas_secret: Optional[str] = Field(default=None)
+ lnbits_saas_instance_id: Optional[str] = Field(default=None)
+
# users
lnbits_admin_users: List[str] = Field(default=[])
lnbits_allowed_users: List[str] = Field(default=[])
@@ -230,11 +237,28 @@ async def check_admin_settings():
http = "https" if settings.lnbits_force_https else "http"
user = settings.lnbits_admin_users[0]
- logger.warning(
- f" ✔️ Access admin user account at: {http}://{settings.host}:{settings.port}/wallet?usr={user}"
- )
+
+ admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ logger.warning(f"✔️ Access admin user account at: {admin_url}")
+
+ if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id:
+ with httpx.Client() as client:
+ headers = {
+ "Content-Type": "application/json; charset=utf-8",
+ "X-API-KEY": settings.lnbits_saas_secret
+ }
+ payload = {
+ "instance_id": settings.lnbits_saas_instance_id,
+ "adminuser": user
+ }
+ try:
+ r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload)
+ logger.warning("sent admin user to saas application")
+ except:
+ logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}")
+
except:
- logger.warning("admin.settings tables does not exist.")
+ logger.error("admin.settings tables does not exist.")
raise
From 1f386a7bc34800a401e40b6653648e670aeb041e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 13:51:35 +0300
Subject: [PATCH 0158/1058] feat: sell fixed amount token
---
lnbits/extensions/cashu/mint.py | 2 +-
lnbits/extensions/cashu/mint_helper.py | 8 +-
lnbits/extensions/cashu/static/js/base64.js | 73 +++++++++----------
.../cashu/templates/cashu/wallet.html | 64 ++++++++--------
4 files changed, 72 insertions(+), 75 deletions(-)
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index 3388b45a..883d0fa9 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -71,7 +71,7 @@ async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
fees_msat = await check_fees(cashu.wallet, invoice_obj)
assert total_provided >= amount + fees_msat / 1000, Exception(
- "provided proofs not enough for Lightning payment."
+ f"Provided proofs (${total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)."
)
await pay_invoice(
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 3892c67f..4db92946 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -33,19 +33,21 @@ def derive_pubkeys(keys: List[PrivateKey]):
# async required?
async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
"""Verifies that the proof of promise was issued by this ledger."""
- # if proof.secret in proofs_used:
- # raise Exception(f"tokens already spent. Secret: {proof.secret}")
+ if proof.secret in proofs_used:
+ raise Exception(f"tokens already spent. Secret: {proof.secret}")
secret_key = derive_keys(master_prvkey)[
proof.amount
] # Get the correct key to check against
C = PublicKey(bytes.fromhex(proof.C), raw=True)
- secret = base64.urlsafe_b64decode(proof.secret)
+ secret = base64.standard_b64decode(proof.secret)
print('### secret', secret)
validMintSig = verify(secret_key, C, secret)
if validMintSig != True:
raise Exception(f"tokens not valid. Secret: {proof.secret}")
+
+
def verify_split_amount(amount: int):
"""Split amount like output amount can't be negative or too big."""
diff --git a/lnbits/extensions/cashu/static/js/base64.js b/lnbits/extensions/cashu/static/js/base64.js
index 2cd19a83..b150882f 100644
--- a/lnbits/extensions/cashu/static/js/base64.js
+++ b/lnbits/extensions/cashu/static/js/base64.js
@@ -1,40 +1,37 @@
-function unescapeBase64Url (str) {
- return (str + '==='.slice((str.length + 3) % 4))
- .replace(/-/g, '+')
- .replace(/_/g, '/')
- }
-
- function escapeBase64Url (str) {
- return str.replace(/\+/g, '-')
- .replace(/\//g, '_')
- .replace(/=/g, '')
+function unescapeBase64Url(str) {
+ return (str + '==='.slice((str.length + 3) % 4))
+ .replace(/-/g, '+')
+ .replace(/_/g, '/')
+}
+
+function escapeBase64Url(str) {
+ return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
+}
+
+const uint8ToBase64 = (function (exports) {
+ 'use strict'
+
+ var fromCharCode = String.fromCharCode
+ var encode = function encode(uint8array) {
+ var output = []
+
+ for (var i = 0, length = uint8array.length; i < length; i++) {
+ output.push(fromCharCode(uint8array[i]))
+ }
+
+ return btoa(output.join(''))
}
- const uint8ToBase64 = (function (exports) {
- 'use strict';
-
- var fromCharCode = String.fromCharCode;
- var encode = function encode(uint8array) {
- var output = [];
-
- for (var i = 0, length = uint8array.length; i < length; i++) {
- output.push(fromCharCode(uint8array[i]));
- }
-
- return btoa(output.join(''));
- };
-
- var asCharCode = function asCharCode(c) {
- return c.charCodeAt(0);
- };
-
- var decode = function decode(chars) {
- return Uint8Array.from(atob(chars), asCharCode);
- };
-
- exports.decode = decode;
- exports.encode = encode;
-
- return exports;
-
- }({}));
\ No newline at end of file
+ var asCharCode = function asCharCode(c) {
+ return c.charCodeAt(0)
+ }
+
+ var decode = function decode(chars) {
+ return Uint8Array.from(atob(chars), asCharCode)
+ }
+
+ exports.decode = decode
+ exports.encode = encode
+
+ return exports
+})({})
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0d250a62..5da41d0a 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -787,7 +787,7 @@ page_container %}
const secret = nobleSecp256k1.utils.randomBytes(32)
// const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
// todo: base64Url
- const encodedSecret = uint8ToBase64.encode(secret)
+ const encodedSecret = uint8ToBase64.encode(secret)
secrets.push(encodedSecret)
const {B_, randomBlindingFactor} = await step1Bob(secret)
randomBlindingFactors.push(randomBlindingFactor)
@@ -852,47 +852,45 @@ page_container %}
sellTokens: async function () {
console.log('#### sell tokens')
const amount = this.sellData.invoice.sat
- const token = this.tokens
- .filter(t => t.promises?.length)
- .find(t => t.promises.find(b => b.amount === amount))
- console.log('### token', token)
- if (token) {
- const promiseIndex = token.promises
- .map(p => `${p.amount}`)
- .indexOf(`${amount}`)
- const promise = token.promises[promiseIndex]
- console.log('### promise', promise)
+ const paidTokens = this.tokens.filter(t => t.promises?.length)
+ console.log('### paidTokens', paidTokens)
+ const proofs = paidTokens.map(token => {
+ // const promiseIndex = token.promises
+ // .map(p => `${p.amount}`)
+ // .indexOf(`${amount}`)
+ return token.promises.map((promise, promiseIndex) => {
+ // const promise = token.promises[promiseIndex]
+ console.log('### promise', promise)
- const secret = token.secrets[promiseIndex]
- const randomBlindingFactor = token.randomBlindingFactors[promiseIndex]
+ const secret = token.secrets[promiseIndex]
+ const randomBlindingFactor =
+ token.randomBlindingFactors[promiseIndex]
- const C_ = nobleSecp256k1.Point.fromHex(promise['C_'])
- const A = this.keys[promise.amount] // todo
+ const C_ = nobleSecp256k1.Point.fromHex(promise['C_'])
+ const A = this.keys[promise.amount] // todo
- console.log('#### C_', C_)
- console.log('#### A', A)
+ console.log('#### C_', C_)
+ console.log('#### A', A)
- const C = step3Bob(
- C_,
- randomBlindingFactor,
- nobleSecp256k1.Point.fromHex(A)
- )
+ const C = step3Bob(
+ C_,
+ randomBlindingFactor,
+ nobleSecp256k1.Point.fromHex(A)
+ )
- const proofs = [
- {
- amount,
+ return {
+ amount: promise.amount,
secret,
C: C.toHex(true)
}
- ]
-
- const payload = {
- proofs,
- amount,
- invoice: this.sellData.bolt11
- }
- console.log('#### payload', JSON.stringify(payload))
+ })
+ })
+ const payload = {
+ proofs: proofs.flat(),
+ amount,
+ invoice: this.sellData.bolt11
}
+ console.log('#### payload', JSON.stringify(payload))
},
fetchMintKeys: async function () {
From f0cc78dc475d9f846878c11e6bef225fb8000f3b Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 14:05:08 +0300
Subject: [PATCH 0159/1058] feat: add tabs
---
.../cashu/templates/cashu/wallet.html | 177 ++++++++++++------
1 file changed, 124 insertions(+), 53 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 5da41d0a..cad45765 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -69,61 +69,131 @@ page_container %}
-
- {% raw %}
-
-
-
-
-
+
+
+
+
+
+
+
+ {% raw %}
+
+
+
+
+
+ Pending
+
+
+ Recheck
+
+
+
+
+
+
+
- Pending
-
-
- Recheck
-
-
-
-
-
-
-
- {{props.row.amount}}
-
+ {{props.row.amount}}
+
-
- {{props.row.memo}}
-
-
- {{props.row.date}}
-
-
- {{props.row.hash}}
-
-
-
- {% endraw %}
-
+
+ {{props.row.memo}}
+
+
+ {{props.row.date}}
+
+
+ {{props.row.hash}}
+
+
+
+ {% endraw %}
+
+
+
+
+ {% raw %}
+
+
+
+
+
+ Pending
+
+
+ Recheck
+
+
+
+
+
+
+
+ {{props.row.amount}}
+
+
+
+ {{props.row.memo}}
+
+
+ {{props.row.date}}
+
+
+ {{props.row.hash}}
+
+
+
+ {% endraw %}
+
+
+
+
+ History
+
+
@@ -331,6 +401,7 @@ page_container %}
showInvoiceDetails: false,
showPayInvoice: false,
tokens: [],
+ tab: 'tokens',
receive: {
show: false,
From 1c0a889d97bb67df94dab4ccfb78d3958812ab56 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 14:55:34 +0300
Subject: [PATCH 0160/1058] feat: simple tokenList
---
.../cashu/templates/cashu/wallet.html | 114 ++++++++++++------
1 file changed, 77 insertions(+), 37 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index cad45765..7b5e1c3f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -79,54 +79,31 @@ page_container %}
{% raw %}
-
-
-
- Pending
-
-
- Recheck
-
-
-
-
-
-
- {{props.row.amount}}
+ {{props.row.denomination}}
+
+
+ {{props.row.count}}
+
+
+ {{props.row.value}}
-
{{props.row.memo}}
-
- {{props.row.date}}
-
-
- {{props.row.hash}}
-
{% endraw %}
@@ -475,6 +452,44 @@ page_container %}
},
filter: null
},
+
+ tokensTable: {
+ columns: [
+ {
+ name: 'denomination',
+ align: 'left',
+ label: 'Denomination',
+ field: 'denomination',
+ sortable: true
+ },
+ {
+ name: 'count',
+ align: 'left',
+ label: 'Count',
+ field: 'count',
+ sortable: true
+ },
+ {
+ name: 'value',
+ align: 'left',
+ label: 'Value',
+ field: 'value',
+ sortable: true
+ },
+ {
+ name: 'memo',
+ align: 'left',
+ label: 'Memo',
+ field: 'memo',
+ sortable: true
+ }
+ ],
+ pagination: {
+ rowsPerPage: 10
+ },
+ filter: null
+ },
+
paymentsChart: {
show: false
},
@@ -498,6 +513,31 @@ page_container %}
},
pendingPaymentsExist: function () {
return this.payments.findIndex(payment => payment.pending) !== -1
+ },
+
+ tokenList: function () {
+ const y = {}
+ let x = this.tokens
+ .map(t => t.blindedMessages)
+ .flat()
+ .map(t => ({
+ blindingFactor: t.B_,
+ denomination: t.amount
+ }))
+ console.log('### x1', x)
+ x.forEach(t => {
+ y[`_${t.denomination}`] = y[`_${t.denomination}`] || []
+ y[`_${t.denomination}`].push(t)
+ })
+ x = Object.keys(y).map(k => ({
+ denomination: y[k][0].denomination,
+ count: y[k].length,
+ value: y[k][0].denomination * y[k].length
+ }))
+ console.log('### x2', x)
+ console.log('### y', y)
+
+ return x
}
},
filters: {
From 44d04f1364c15a8ddbdd90c7f10f58c2acc228bf Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 14:59:20 +0300
Subject: [PATCH 0161/1058] refactor: simplify code
---
.../cashu/templates/cashu/wallet.html | 23 ++++++++-----------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 7b5e1c3f..16f8e163 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -524,20 +524,17 @@ page_container %}
blindingFactor: t.B_,
denomination: t.amount
}))
- console.log('### x1', x)
- x.forEach(t => {
- y[`_${t.denomination}`] = y[`_${t.denomination}`] || []
- y[`_${t.denomination}`].push(t)
- })
- x = Object.keys(y).map(k => ({
- denomination: y[k][0].denomination,
- count: y[k].length,
- value: y[k][0].denomination * y[k].length
- }))
- console.log('### x2', x)
- console.log('### y', y)
+ .reduce((y, t) => {
+ y[`_${t.denomination}`] = y[`_${t.denomination}`] || []
+ y[`_${t.denomination}`].push(t)
+ return y
+ }, {})
- return x
+ return Object.keys(x).map(k => ({
+ denomination: x[k][0].denomination,
+ count: x[k].length,
+ value: x[k][0].denomination * x[k].length
+ }))
}
},
filters: {
From d45dfbd28310e857e250a473453300b5b12813bf Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 15:06:10 +0300
Subject: [PATCH 0162/1058] feat: show balance
---
.../cashu/templates/cashu/wallet.html | 25 ++++++++++---------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 16f8e163..d0e90493 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -22,7 +22,7 @@ page_container %}
- {% raw %} {{balanceAmount}}
+ {% raw %} {{balance}}
{{tickershort}}{% endraw %}
@@ -357,7 +357,6 @@ page_container %}
mixins: [windowMixin],
data: function () {
return {
- balanceAmount: '',
tickershort: '',
name: '',
@@ -497,7 +496,7 @@ page_container %}
show: false,
location: window.location
},
- balance: 0,
+
credit: 0,
newName: ''
}
@@ -516,8 +515,8 @@ page_container %}
},
tokenList: function () {
- const y = {}
- let x = this.tokens
+ const x = this.tokens
+ .filter(t => t.promises?.length)
.map(t => t.blindedMessages)
.flat()
.map(t => ({
@@ -535,6 +534,14 @@ page_container %}
count: x[k].length,
value: x[k][0].denomination * x[k].length
}))
+ },
+
+ balance: function () {
+ return this.tokens
+ .filter(t => t.promises?.length)
+ .map(t => t.blindedMessages)
+ .flat()
+ .reduce((sum, el) => (sum += el.amount), 0)
}
},
filters: {
@@ -584,9 +591,7 @@ page_container %}
this.parse.data.paymentChecker = null
this.parse.camera.show = false
},
- updateBalance: function (credit) {
- this.balance = this.balance // update balance
- },
+
closeReceiveDialog: function () {
setTimeout(() => {
clearInterval(this.receive.paymentChecker)
@@ -1043,10 +1048,6 @@ page_container %}
this.tickershort = this.$q.localStorage.getItem('cashu.tickershort')
}
- if (!this.$q.localStorage.getItem('cashu.amount')) {
- this.balanceAmount = 0
- }
-
// get mint
if (params.get('mint_id')) {
this.mintId = params.get('mint_id')
From 376b914c7cdcb01d0f31fb1cd2e99bead42abb09 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 15:36:24 +0300
Subject: [PATCH 0163/1058] feat: send tokens UI
---
.../cashu/templates/cashu/wallet.html | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index d0e90493..385143e1 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -63,6 +63,7 @@ page_container %}
rounded
color="primary"
class="full-width"
+ @click="showSendTokensDialog"
>
Send
@@ -266,6 +267,58 @@ page_container %}
+
+
+
+
+
+ How much would you like to send?
+
+
+
+
+
+
+
+
+
+
+ Show Tokens
+
+ Close
+
+
+
+
@@ -374,8 +427,14 @@ page_container %}
invoice: '',
bolt11: ''
},
+ sendData: {
+ amount: 0,
+ memo: '',
+ tokens: ''
+ },
showInvoiceDetails: false,
showPayInvoice: false,
+ showSendTokens: false,
tokens: [],
tab: 'tokens',
@@ -797,6 +856,13 @@ page_container %}
this.showPayInvoice = true
},
+ showSendTokensDialog: function () {
+ this.sendData.tokens = ''
+ this.sendData.amount = 0
+ this.sendData.memo = ''
+ this.showSendTokens = true
+ },
+
requestInvoice: async function () {
try {
const {data} = await LNbits.api.request(
@@ -892,6 +958,10 @@ page_container %}
await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
},
+ buildAnsShowTokens: async function () {
+ this.sendData.tokens = 'toookeeens:' + this.sendData.amount
+ },
+
buildTokens: async function (amounts, paymentHash) {
const blindedMessages = []
const secrets = []
From b2ca749ff371d88a9964e75265a5a95b1d5be2da Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 15:55:18 +0300
Subject: [PATCH 0164/1058] refactor: re-order methods
---
.../extensions/cashu/templates/cashu/wallet.html | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 385143e1..641b704c 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -931,6 +931,14 @@ page_container %}
}
},
+ requestTokens: async function (amounts, paymentHash) {
+ const newTokens = await this.buildTokens(amounts, paymentHash)
+ this.tokens.push(newTokens)
+ this.storeTokens()
+ console.log('### this.tokens', this.tokens)
+ await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
+ },
+
fetchPromisesFromMint: async function (hash, blindedMessages) {
console.log('### fetchPromisesFromMint', hash, blindedMessages)
try {
@@ -950,14 +958,6 @@ page_container %}
}
},
- requestTokens: async function (amounts, paymentHash) {
- const newTokens = await this.buildTokens(amounts, paymentHash)
- this.tokens.push(newTokens)
- this.storeTokens()
- console.log('### this.tokens', this.tokens)
- await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
- },
-
buildAnsShowTokens: async function () {
this.sendData.tokens = 'toookeeens:' + this.sendData.amount
},
From a95e62406dc893f44e20dd3e97e3f60f31d08197 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 17:42:47 +0300
Subject: [PATCH 0165/1058] feat: select coin limitted
---
lnbits/extensions/cashu/mint.py | 2 +-
.../cashu/templates/cashu/wallet.html | 99 ++++++++++++++-----
2 files changed, 77 insertions(+), 24 deletions(-)
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
index 883d0fa9..4153fb30 100644
--- a/lnbits/extensions/cashu/mint.py
+++ b/lnbits/extensions/cashu/mint.py
@@ -71,7 +71,7 @@ async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
fees_msat = await check_fees(cashu.wallet, invoice_obj)
assert total_provided >= amount + fees_msat / 1000, Exception(
- f"Provided proofs (${total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)."
+ f"Provided proofs ({total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)."
)
await pay_invoice(
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 641b704c..02358d3d 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -306,7 +306,7 @@ page_container %}
Show Tokens p.amount === amount)
+ if (index >= 0) {
+ return {
+ promise: token.promises[index],
+ secret: token.secrets[index],
+ randomBlindingFactor: token.randomBlindingFactors[index]
+ }
+ }
+ }
+ },
+
+ constructProof: function (token) {},
+
buildTokens: async function (amounts, paymentHash) {
const blindedMessages = []
const secrets = []
@@ -1038,34 +1072,19 @@ page_container %}
const paidTokens = this.tokens.filter(t => t.promises?.length)
console.log('### paidTokens', paidTokens)
const proofs = paidTokens.map(token => {
- // const promiseIndex = token.promises
- // .map(p => `${p.amount}`)
- // .indexOf(`${amount}`)
return token.promises.map((promise, promiseIndex) => {
- // const promise = token.promises[promiseIndex]
console.log('### promise', promise)
const secret = token.secrets[promiseIndex]
const randomBlindingFactor =
token.randomBlindingFactors[promiseIndex]
- const C_ = nobleSecp256k1.Point.fromHex(promise['C_'])
- const A = this.keys[promise.amount] // todo
-
- console.log('#### C_', C_)
- console.log('#### A', A)
-
- const C = step3Bob(
- C_,
- randomBlindingFactor,
- nobleSecp256k1.Point.fromHex(A)
- )
-
- return {
- amount: promise.amount,
+ return this.promiseToProof(
+ promise.amount,
+ promise['C_'],
secret,
- C: C.toHex(true)
- }
+ randomBlindingFactor
+ )
})
})
const payload = {
@@ -1074,6 +1093,40 @@ page_container %}
invoice: this.sellData.bolt11
}
console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/cashu/${this.mintId}/melt`,
+ '',
+ payload
+ )
+ this.$q.notify({
+ timeout: 5000,
+ message: 'Invoice paid'
+ })
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
+ },
+
+ // C_hex = promise['C_']
+ // amount = promise.amount
+ promiseToProof: function (amount, C_hex, secret, randomBlindingFactor) {
+ const C_ = nobleSecp256k1.Point.fromHex(C_hex)
+ const A = this.keys[amount]
+
+ const C = step3Bob(
+ C_,
+ randomBlindingFactor,
+ nobleSecp256k1.Point.fromHex(A)
+ )
+
+ return {
+ amount,
+ secret,
+ C: C.toHex(true)
+ }
},
fetchMintKeys: async function () {
From ae9ab55fae2e6bac812c484c6df0e38f0bd45f36 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 18:09:07 +0300
Subject: [PATCH 0166/1058] chore: aahaaa\
---
.../cashu/templates/cashu/wallet.html | 30 ++++++++++++++++---
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 02358d3d..0da29d78 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -281,7 +281,7 @@ page_container %}
filled
dense
v-model.number="sendData.amount"
- label="Amount (sats) *"
+ label="Amount (tokens) *"
type="number"
class="q-mb-lg"
>
@@ -297,7 +297,7 @@ page_container %}
Show Tokens
+ Burn Tokens
Close t.promises?.length && t.secrets.find(s => s == token.secret)
+ )
+ if (secretIndex >= 0) {
+ token.blindedMessages?.splice(secretIndex, 1)
+ token.promises?.splice(secretIndex, 1)
+ token.randomBlindingFactors?.splice(secretIndex, 1)
+ token.secrets?.splice(secretIndex, 1)
+ }
+ }
+ console.log('### this.tokens', this.tokens)
+ },
+
findTokenForAmount: function (amount) {
for (const token of this.tokens) {
const index = token.promises?.findIndex(p => p.amount === amount)
From 94dcfdb16c0960f9cbce65d7df2d63c0b065901f Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 18:24:29 +0300
Subject: [PATCH 0167/1058] feat: burn in hell token
---
.../cashu/templates/cashu/wallet.html | 26 ++++++++++++-------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0da29d78..301f6e6f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -989,17 +989,25 @@ page_container %}
},
burnTokens: function () {
- for (const token of this.sendData.tokens) {
- const secretIndex = this.tokens.findIndex(
- t => t.promises?.length && t.secrets.find(s => s == token.secret)
- )
- if (secretIndex >= 0) {
- token.blindedMessages?.splice(secretIndex, 1)
- token.promises?.splice(secretIndex, 1)
- token.randomBlindingFactors?.splice(secretIndex, 1)
- token.secrets?.splice(secretIndex, 1)
+ for (const sentToken of this.sendData.tokens) {
+ for (const token of this.tokens) {
+ if (token.status === 'paid') {
+ const secretIndex = token.secrets.findIndex(
+ s => s === sentToken.secret
+ )
+ console.log('### secretIndex', secretIndex)
+ if (secretIndex >= 0) {
+ token.blindedMessages?.splice(secretIndex, 1)
+ token.promises?.splice(secretIndex, 1)
+ token.randomBlindingFactors?.splice(secretIndex, 1)
+ token.secrets?.splice(secretIndex, 1)
+ // todo: persist
+ return
+ }
+ }
}
}
+
console.log('### this.tokens', this.tokens)
},
From 5bf225acd48e39d28926f49c3e5e7998926470f1 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 18:28:07 +0300
Subject: [PATCH 0168/1058] fix: do not stop secret search
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 301f6e6f..14290570 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1001,13 +1001,17 @@ page_container %}
token.promises?.splice(secretIndex, 1)
token.randomBlindingFactors?.splice(secretIndex, 1)
token.secrets?.splice(secretIndex, 1)
- // todo: persist
- return
}
}
}
}
+ this.$q.notify({
+ timeout: 5000,
+ message: 'Tokens burned'
+ })
+ this.storeTokens()
+ this.showSendTokens = false
console.log('### this.tokens', this.tokens)
},
From ccec86ec7fb1cae3026df0e44e88ce22e6357e06 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 18:44:28 +0300
Subject: [PATCH 0169/1058] feat: show receive dialog
---
.../cashu/templates/cashu/wallet.html | 60 ++++++++++++++++++-
1 file changed, 58 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 14290570..6cf394a0 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -52,6 +52,7 @@ page_container %}
rounded
color="primary"
class="full-width"
+ @click="showReceiveTokensDialog"
>Receive
@@ -322,6 +323,43 @@ page_container %}
+
+
+
+
+
+
+
+ Paste tokens please
+
+
+
+
+
+
+ Accept Tokens
+ Close
+
+
+
+
+
@@ -436,9 +474,13 @@ page_container %}
tokens: '',
tokensBase64: ''
},
+ receiveData: {
+ tokensBase64: ''
+ },
showInvoiceDetails: false,
showPayInvoice: false,
showSendTokens: false,
+ showReceiveTokens: false,
tokens: [],
tab: 'tokens',
@@ -868,6 +910,11 @@ page_container %}
this.showSendTokens = true
},
+ showReceiveTokensDialog : function() {
+ this.receiveData.tokensBase64 = ''
+ this.showReceiveTokens = true
+ },
+
requestInvoice: async function () {
try {
const {data} = await LNbits.api.request(
@@ -1015,14 +1062,23 @@ page_container %}
console.log('### this.tokens', this.tokens)
},
+ acceptTokens: async function() {
+ this.showReceiveTokens = false
+ console.log('### receive tokens', this.receiveData.tokensBase64)
+ if (this.receiveData.tokensBase64) {
+ const tokensJson = atob(this.receiveData.tokensBase64)
+ console.log('tokensJson', tokensJson)
+ }
+
+ },
+
findTokenForAmount: function (amount) {
for (const token of this.tokens) {
const index = token.promises?.findIndex(p => p.amount === amount)
if (index >= 0) {
return {
promise: token.promises[index],
- secret: token.secrets[index],
- randomBlindingFactor: token.randomBlindingFactors[index]
+ secret: token.secrets[index]
}
}
}
From 8f40422f88e72a30af67e64fec5115a369d2811e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 11 Oct 2022 21:37:08 +0300
Subject: [PATCH 0170/1058] feat: try to accept tokens
---
.../cashu/templates/cashu/wallet.html | 75 +++++++++++++------
1 file changed, 54 insertions(+), 21 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 6cf394a0..af12182b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -323,16 +323,12 @@ page_container %}
-
-
- Paste tokens please
+ Paste tokens please
-
Accept Tokens
-
@@ -910,7 +902,7 @@ page_container %}
this.showSendTokens = true
},
- showReceiveTokensDialog : function() {
+ showReceiveTokensDialog: function () {
this.receiveData.tokensBase64 = ''
this.showReceiveTokens = true
},
@@ -989,11 +981,11 @@ page_container %}
this.tokens.push(newTokens)
this.storeTokens()
console.log('### this.tokens', this.tokens)
- await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
+ // await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
},
fetchPromisesFromMint: async function (hash, blindedMessages) {
- console.log('### fetchPromisesFromMint', hash, blindedMessages)
+ console.log('### promises', hash, blindedMessages)
try {
const {data} = await LNbits.api.request(
'POST',
@@ -1003,7 +995,7 @@ page_container %}
blinded_messages: blindedMessages
}
)
- console.log('### fetchPromisesFromMint data', data)
+ console.log('### promises data', data)
return data
} catch (error) {
console.error(error)
@@ -1030,9 +1022,16 @@ page_container %}
}
}
console.log('### sendTokens', sendTokens)
- this.sendData.tokens = sendTokens
- this.sendData.tokensBase64 = btoa(JSON.stringify(sendTokens))
- return sendTokens
+ this.sendData.tokens = sendTokens.map(t => {
+ return this.promiseToProof(
+ t.promise.amount,
+ t.promise['C_'],
+ t.secret,
+ t.randomBlindingFactor
+ )
+ })
+ console.log('### this.sendData.tokens', this.sendData.tokens)
+ this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
},
burnTokens: function () {
@@ -1062,14 +1061,47 @@ page_container %}
console.log('### this.tokens', this.tokens)
},
- acceptTokens: async function() {
+ acceptTokens: async function () {
this.showReceiveTokens = false
console.log('### receive tokens', this.receiveData.tokensBase64)
if (this.receiveData.tokensBase64) {
const tokensJson = atob(this.receiveData.tokensBase64)
- console.log('tokensJson', tokensJson)
+ const proofs = JSON.parse(tokensJson)
+ const amount = proofs.reduce((s, t) => (s += t.amount), 0)
+ const amounts = splitAmount(amount)
+ const newTokens = await this.buildTokens(amounts)
+ console.log('newTokens', newTokens)
+
+ const payload = {
+ amount,
+ proofs,
+ outputs: {
+ blinded_messages: newTokens.blindedMessages
+ }
+ }
+
+ console.log('payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/cashu/${this.mintId}/split`,
+ '',
+ payload
+ )
+
+ newTokens.promises = data.snd;
+ // console.log('split data', JSON.stringify(data.snd))
+ // for (let i =0 ;i < newTokens.length; i++) {
+ // Object.assign(newTokens[i], promises)
+ // }
+ console.log('newTokens 2', newTokens)
+ this.tokens.push(newTokens)
+ this.storeTokens()
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
}
-
},
findTokenForAmount: function (amount) {
@@ -1078,7 +1110,8 @@ page_container %}
if (index >= 0) {
return {
promise: token.promises[index],
- secret: token.secrets[index]
+ secret: token.secrets[index],
+ randomBlindingFactor: token.randomBlindingFactors[index]
}
}
}
From 6accf8f7ee74def9b55289dd944f9159b1b63674 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Wed, 12 Oct 2022 08:03:23 +0300
Subject: [PATCH 0171/1058] chore: code format
---
lnbits/extensions/cashu/core/b_dhke.py | 22 +++++++++----------
lnbits/extensions/cashu/mint_helper.py | 6 ++---
.../cashu/templates/cashu/wallet.html | 2 +-
3 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/lnbits/extensions/cashu/core/b_dhke.py b/lnbits/extensions/cashu/core/b_dhke.py
index 8855481c..ff0bc515 100644
--- a/lnbits/extensions/cashu/core/b_dhke.py
+++ b/lnbits/extensions/cashu/core/b_dhke.py
@@ -30,17 +30,17 @@ import hashlib
from secp256k1 import PrivateKey, PublicKey
-def hash_to_curve(message: bytes):
- """Generates a point from the message hash and checks if the point lies on the curve.
- If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
- point = None
- msg_to_hash = message
- while point is None:
- try:
- _hash = hashlib.sha256(msg_to_hash).digest()
- point = PublicKey(b"\x02" + _hash, raw=True)
- except:
- msg_to_hash = _hash
+def hash_to_curve(message: bytes):
+ """Generates a point from the message hash and checks if the point lies on the curve.
+ If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
+ point = None
+ msg_to_hash = message
+ while point is None:
+ try:
+ _hash = hashlib.sha256(msg_to_hash).digest()
+ point = PublicKey(b"\x02" + _hash, raw=True)
+ except:
+ msg_to_hash = _hash
return point
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
index 4db92946..8e7e2275 100644
--- a/lnbits/extensions/cashu/mint_helper.py
+++ b/lnbits/extensions/cashu/mint_helper.py
@@ -1,5 +1,5 @@
-import hashlib
import base64
+import hashlib
from typing import List, Set
from .core.b_dhke import verify
@@ -41,13 +41,11 @@ async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
] # Get the correct key to check against
C = PublicKey(bytes.fromhex(proof.C), raw=True)
secret = base64.standard_b64decode(proof.secret)
- print('### secret', secret)
+ print("### secret", secret)
validMintSig = verify(secret_key, C, secret)
if validMintSig != True:
raise Exception(f"tokens not valid. Secret: {proof.secret}")
-
-
def verify_split_amount(amount: int):
"""Split amount like output amount can't be negative or too big."""
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index af12182b..f64a19b7 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1089,7 +1089,7 @@ page_container %}
payload
)
- newTokens.promises = data.snd;
+ newTokens.promises = data.snd
// console.log('split data', JSON.stringify(data.snd))
// for (let i =0 ;i < newTokens.length; i++) {
// Object.assign(newTokens[i], promises)
From c83b4b6737d7ba7ea024c15516dfab2d4e1a2d4d Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Wed, 12 Oct 2022 08:27:14 +0300
Subject: [PATCH 0172/1058] chore: add extension by default
---
lnbits/extensions/cashu/config.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index 4f097e8b..e798e2ef 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -2,5 +2,6 @@
"name": "Cashu Ecash",
"short_description": "Ecash mints with LN peg in/out",
"icon": "approval",
- "contributors": ["arcbtc", "calle"]
+ "contributors": ["arcbtc", "calle"],
+ "hidden": true
}
From af3f1a82cbbcbce1933251794809ea1d78058d0e Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Wed, 12 Oct 2022 08:32:41 +0300
Subject: [PATCH 0173/1058] chore: disable extension by default
---
lnbits/extensions/cashu/config.json | 2 +-
lnbits/extensions/cashu/config.json.example | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 lnbits/extensions/cashu/config.json.example
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index e798e2ef..fe802034 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -3,5 +3,5 @@
"short_description": "Ecash mints with LN peg in/out",
"icon": "approval",
"contributors": ["arcbtc", "calle"],
- "hidden": true
+ "hidden": false
}
diff --git a/lnbits/extensions/cashu/config.json.example b/lnbits/extensions/cashu/config.json.example
new file mode 100644
index 00000000..e798e2ef
--- /dev/null
+++ b/lnbits/extensions/cashu/config.json.example
@@ -0,0 +1,7 @@
+{
+ "name": "Cashu Ecash",
+ "short_description": "Ecash mints with LN peg in/out",
+ "icon": "approval",
+ "contributors": ["arcbtc", "calle"],
+ "hidden": true
+}
From 0873c03b7f5b776db1c53b79146c5a7b7329a91e Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 12 Oct 2022 06:46:50 +0100
Subject: [PATCH 0174/1058] missing comma
---
lnbits/extensions/cashu/migrations.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index d06baef1..0cc6a7d4 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -42,7 +42,7 @@ async def m001_initial(db):
amount INT,
B_b TEXT NOT NULL,
C_b TEXT NOT NULL,
- cashu_id TEXT NOT NULL
+ cashu_id TEXT NOT NULL,
UNIQUE (B_b)
);
"""
From 71116394460ccda0ca8124523629f1316c7208cd Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 12 Oct 2022 06:50:36 +0100
Subject: [PATCH 0175/1058] added props
---
lnbits/extensions/cashu/config.json | 2 +-
lnbits/extensions/cashu/templates/cashu/_cashu.html | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index fe802034..5262ea62 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -2,6 +2,6 @@
"name": "Cashu Ecash",
"short_description": "Ecash mints with LN peg in/out",
"icon": "approval",
- "contributors": ["arcbtc", "calle"],
+ "contributors": ["arcbtc", "calle", "vlad"],
"hidden": false
}
diff --git a/lnbits/extensions/cashu/templates/cashu/_cashu.html b/lnbits/extensions/cashu/templates/cashu/_cashu.html
index f5af738f..03457ac7 100644
--- a/lnbits/extensions/cashu/templates/cashu/_cashu.html
+++ b/lnbits/extensions/cashu/templates/cashu/_cashu.html
@@ -1,4 +1,4 @@
-
+
@@ -7,7 +7,7 @@
Created by
- Calle . arcbtc, vlad , calle .
From c9ead25d50cc1c8cd019e51d7147c4febb71b635 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Wed, 12 Oct 2022 13:08:59 +0200
Subject: [PATCH 0176/1058] bugfixes and fix topup wallet
---
lnbits/app.py | 1 +
.../admin/templates/admin/_tab_funding.html | 4 +--
.../admin/templates/admin/index.html | 28 +++++++++------
lnbits/extensions/admin/views_api.py | 36 ++++++++++---------
lnbits/server.py | 1 +
lnbits/settings.py | 29 ++++++++++-----
lnbits/wallets/fake.py | 2 +-
7 files changed, 60 insertions(+), 41 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 6fa5cf5d..49ad8d77 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -84,6 +84,7 @@ async def check_funding_source() -> None:
def signal_handler(signal, frame):
logger.debug(f"SIGINT received, terminating LNbits.")
sys.exit(1)
+
signal.signal(signal.SIGINT, signal_handler)
WALLET = get_wallet_class()
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index 8b5456f1..34162a9f 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -8,9 +8,7 @@
Funding Source Info
{%raw%}
-
- Funding Source: {{settings.lnbits_backend_wallet_class}}
-
+ Funding Source: {{settings.lnbits_backend_wallet_class}}
Balance: {{balance / 1000}} sats
{%endraw%}
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d8111595..d3e93a71 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -3,7 +3,13 @@
-
+
u !== user
- )
+ this.settings.lnbits_admin_users = admin_users.filter(u => u !== user)
},
addAllowedUser() {
let addUser = this.formData.allowed_users_add
@@ -155,7 +159,9 @@
},
removeAllowedUser(user) {
let allowed_users = this.settings.lnbits_allowed_users
- this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user)
+ this.settings.lnbits_allowed_users = allowed_users.filter(
+ u => u !== user
+ )
},
addAdSpace() {
let adSpace = this.formData.ad_space_add
@@ -187,10 +193,10 @@
topupWallet() {
LNbits.api
.request(
- 'POST',
+ 'PUT',
'/admin/api/v1/topup/?usr=' + this.g.user.id,
- this.wallet.id,
- this.wallet.amount
+ this.g.user.wallets[0].adminkey,
+ this.wallet
)
.then(response => {
this.$q.notify({
@@ -209,7 +215,7 @@
})
},
updateSettings() {
- let data = {
+ let data = {
...this.formData
}
LNbits.api
@@ -262,7 +268,7 @@
LNbits.utils.notifyApiError(error)
})
}
- },
+ }
})
{% endblock %}
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index ae2959bc..63ed5b3c 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,7 +1,6 @@
from http import HTTPStatus
-from fastapi import Body, Depends, Request
-from loguru import logger
+from fastapi import Body, Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_wallet
@@ -9,47 +8,50 @@ from lnbits.core.models import User
from lnbits.decorators import check_admin
from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import UpdateSettings
-from lnbits.requestvars import g
from lnbits.server import server_restart
-from lnbits.settings import settings
from .crud import delete_settings, update_settings, update_wallet_balance
-@admin_ext.get("/api/v1/restart/", status_code=HTTPStatus.OK)
-async def api_restart_server(user: User = Depends(check_admin)):
+@admin_ext.get(
+ "/api/v1/restart/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
+async def api_restart_server() -> dict[str, str]:
server_restart.set()
return {"status": "Success"}
-@admin_ext.put("/api/v1/topup/", status_code=HTTPStatus.OK)
+@admin_ext.put(
+ "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
async def api_update_balance(
- wallet_id, topup_amount: int, user: User = Depends(check_admin)
-):
+ id: str = Body(...), amount: int = Body(...)
+) -> dict[str, str]:
try:
- wallet = await get_wallet(wallet_id)
+ await get_wallet(id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist."
)
- await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
+ await update_wallet_balance(wallet_id=id, amount=int(amount))
return {"status": "Success"}
-@admin_ext.put("/api/v1/settings/", status_code=HTTPStatus.OK)
+@admin_ext.put(
+ "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
async def api_update_settings(
- user: User = Depends(check_admin),
data: UpdateSettings = Body(...),
):
settings = await update_settings(data)
return {"status": "Success", "settings": settings.dict()}
-@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK)
-async def api_delete_settings(
- user: User = Depends(check_admin),
-):
+@admin_ext.delete(
+ "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
+async def api_delete_settings() -> dict[str, str]:
await delete_settings()
return {"status": "Success"}
diff --git a/lnbits/server.py b/lnbits/server.py
index 79af8112..6d4cd2e7 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -52,6 +52,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
port=port,
host=host,
reload=reload,
+ forwarded_allow_ips="*",
ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile,
**d
diff --git a/lnbits/settings.py b/lnbits/settings.py
index f183211c..61dbd6f2 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,20 +1,19 @@
import importlib
import json
import subprocess
-import httpx
from os import path
from sqlite3 import Row
from typing import List, Optional
+import httpx
from loguru import logger
from pydantic import BaseSettings, Field, validator
-
def list_parse_fallback(v):
try:
return json.loads(v)
- except Exception as e:
+ except Exception:
replaced = v.replace(" ", "")
if replaced:
return replaced.split(",")
@@ -238,24 +237,36 @@ async def check_admin_settings():
http = "https" if settings.lnbits_force_https else "http"
user = settings.lnbits_admin_users[0]
- admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ admin_url = (
+ f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ )
logger.warning(f"✔️ Access admin user account at: {admin_url}")
- if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id:
+ if (
+ settings.lnbits_saas_callback
+ and settings.lnbits_saas_secret
+ and settings.lnbits_saas_instance_id
+ ):
with httpx.Client() as client:
headers = {
"Content-Type": "application/json; charset=utf-8",
- "X-API-KEY": settings.lnbits_saas_secret
+ "X-API-KEY": settings.lnbits_saas_secret,
}
payload = {
"instance_id": settings.lnbits_saas_instance_id,
- "adminuser": user
+ "adminuser": user,
}
try:
- r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload)
+ client.post(
+ settings.lnbits_saas_callback,
+ headers=headers,
+ json=payload,
+ )
logger.warning("sent admin user to saas application")
except:
- logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}")
+ logger.error(
+ f"error sending admin user to saas: {settings.lnbits_saas_callback}"
+ )
except:
logger.error("admin.settings tables does not exist.")
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 73458e8c..94ff5f48 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -19,7 +19,6 @@ from .base import (
class FakeWallet(Wallet):
- queue: asyncio.Queue = asyncio.Queue(0)
secret: str = settings.fake_wallet_secret
privkey: str = hashlib.pbkdf2_hmac(
"sha256",
@@ -98,6 +97,7 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value: Invoice = await self.queue.get()
yield value.payment_hash
From 9a48b174c62bd1908be3a8698c36b34aa78a3188 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 12 Oct 2022 19:04:46 +0100
Subject: [PATCH 0177/1058] add funding sources options
---
lnbits/app.py | 1 +
lnbits/extensions/admin/models.py | 41 +++-
.../admin/templates/admin/_tab_funding.html | 25 +-
.../admin/templates/admin/index.html | 217 +++++++++++++++++-
4 files changed, 259 insertions(+), 25 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index a8371950..50f218b7 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -84,6 +84,7 @@ async def check_funding_source() -> None:
def signal_handler(signal, frame):
logger.debug(f"SIGINT received, terminating LNbits.")
sys.exit(1)
+
signal.signal(signal.SIGINT, signal_handler)
WALLET = get_wallet_class()
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 94fa56bb..d9d2b22f 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -6,10 +6,10 @@ from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: List[str] = Query(None)
- lnbits_allowed_users: List[str] = Query(None)
- lnbits_admin_ext: List[str] = Query(None)
- lnbits_disabled_ext: List[str] = Query(None)
+ lnbits_admin_users: List[str] = Query(None)
+ lnbits_allowed_users: List[str] = Query(None)
+ lnbits_admin_ext: List[str] = Query(None)
+ lnbits_disabled_ext: List[str] = Query(None)
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -23,4 +23,35 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: List[str] = Query(None)
+ lnbits_ad_space: List[str] = Query(None)
+
+ # funding sources
+ fake_wallet_secret: str = Query(None)
+ lnbits_endpoint: str = Query(None)
+ lnbits_key: str = Query(None)
+ cliche_endpoint: str = Query(None)
+ corelightning_rpc: str = Query(None)
+ eclair_url: str = Query(None)
+ eclair_pass: str = Query(None)
+ lnd_rest_endpoint: str = Query(None)
+ lnd_rest_cert: str = Query(None)
+ lnd_rest_macaroon: str = Query(None)
+ lnd_rest_macaroon_encrypted: str = Query(None)
+ lnd_cert: str = Query(None)
+ lnd_admin_macaroon: str = Query(None)
+ lnd_invoice_macaroon: str = Query(None)
+ lnd_grpc_endpoint: str = Query(None)
+ lnd_grpc_cert: str = Query(None)
+ lnd_grpc_port: int = Query(None, ge=0)
+ lnd_grpc_admin_macaroon: str = Query(None)
+ lnd_grpc_invoice_macaroon: str = Query(None)
+ lnd_grpc_macaroon_encrypted: str = Query(None)
+ lnpay_api_endpoint: str = Query(None)
+ lnpay_api_key: str = Query(None)
+ lnpay_wallet_key: str = Query(None)
+ lntxbot_api_endpoint: str = Query(None)
+ lntxbot_key: str = Query(None)
+ opennode_api_endpoint: str = Query(None)
+ opennode_key: str = Query(None)
+ spark_url: str = Query(None)
+ spark_token: str = Query(None)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index 8b5456f1..a523d4e5 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -8,9 +8,7 @@
Funding Source Info
{%raw%}
-
- Funding Source: {{settings.lnbits_backend_wallet_class}}
-
+ Funding Source: {{settings.lnbits_backend_wallet_class}}
Balance: {{balance / 1000}} sats
{%endraw%}
@@ -60,21 +58,30 @@
- Funding Sources
+ Funding Sources (Requires server restart)
-
+
-
-
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d8111595..ccaddda4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -3,7 +3,13 @@
-
+
u !== user
- )
+ this.settings.lnbits_admin_users = admin_users.filter(u => u !== user)
},
addAllowedUser() {
let addUser = this.formData.allowed_users_add
@@ -155,7 +335,9 @@
},
removeAllowedUser(user) {
let allowed_users = this.settings.lnbits_allowed_users
- this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user)
+ this.settings.lnbits_allowed_users = allowed_users.filter(
+ u => u !== user
+ )
},
addAdSpace() {
let adSpace = this.formData.ad_space_add
@@ -208,8 +390,19 @@
LNbits.utils.notifyApiError(error)
})
},
+ updateFundingData(){
+ this.settings.lnbits_allowed_funding_sources.map(f => {
+ let opts = this.funding_sources.get(f)
+ if (!opts) return
+
+ Object.keys(opts).forEach(e => {
+ opts[e].value = this.settings[e]
+ })
+ })
+ console.log("funding", this.funding_sources)
+ },
updateSettings() {
- let data = {
+ let data = {
...this.formData
}
LNbits.api
@@ -222,11 +415,13 @@
.then(response => {
this.settings = response.data.settings
this.formData = _.clone(this.settings)
+ this.updateFundingData()
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
icon: null
})
+ console.log(this.settings)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -262,7 +457,7 @@
LNbits.utils.notifyApiError(error)
})
}
- },
+ }
})
{% endblock %}
From c36f7a48aadd9c0c64dd63df40358955a3ea1c64 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:16:39 +0200
Subject: [PATCH 0178/1058] lnbits migrations work
---
lnbits/extensions/cashu/__init__.py | 24 ++-
lnbits/extensions/cashu/config.json | 6 +-
lnbits/extensions/cashu/crud.py | 117 +++++++++++++-
lnbits/extensions/cashu/migrations.py | 80 +---------
lnbits/extensions/cashu/tasks.py | 15 ++
.../cashu/templates/cashu/index.html | 6 +-
.../cashu/templates/cashu/wallet.html | 121 +++++++-------
lnbits/extensions/cashu/views_api.py | 147 ++++++++++++++++--
8 files changed, 358 insertions(+), 158 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index cb62ffca..b36e94ef 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 5262ea62..e71e6889 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 c8f3c72b..48b90d70 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 0cc6a7d4..019ee84d 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 fe00a591..9d2d7f15 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 4e5ba911..dcb2b4a7 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 f64a19b7..4f12cd1c 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 %}
@@ -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
@@ -182,11 +180,15 @@ page_container %}
active-class="px-0"
indicator-color="transparent"
>
-
+
-
-
-
+
+
+
@@ -214,7 +216,7 @@ page_container %}
-
+
Copy invoice 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)
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 2f4b5ae4..e786cd2f 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -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
From 79a0c28f23fd225a71a3186dbd6ca59076e38401 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:17:21 +0200
Subject: [PATCH 0179/1058] allow external migration modules
---
lnbits/commands.py | 16 +-
lnbits/extensions/cashu/ledger.py | 351 ------------------------------
lnbits/helpers.py | 4 +
3 files changed, 14 insertions(+), 357 deletions(-)
delete mode 100644 lnbits/extensions/cashu/ledger.py
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 0f7454f2..89739076 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -65,14 +65,14 @@ async def migrate_databases():
(db_name, version, version),
)
- async def run_migration(db, migrations_module):
- db_name = migrations_module.__name__.split(".")[-2]
+ async def run_migration(db, migrations_module, db_name):
for key, migrate in migrations_module.__dict__.items():
match = match = matcher.match(key)
if match:
version = int(match.group(1))
if version > current_versions.get(db_name, 0):
logger.debug(f"running migration {db_name}.{version}")
+ logger.debug(f"db = {db}")
await migrate(db)
if db.schema == None:
@@ -97,20 +97,24 @@ async def migrate_databases():
rows = await (await conn.execute("SELECT * FROM dbversions")).fetchall()
current_versions = {row["db"]: row["version"] for row in rows}
matcher = re.compile(r"^m(\d\d\d)_")
- await run_migration(conn, core_migrations)
+ db_name = core_migrations.__name__.split(".")[-2]
+ await run_migration(conn, core_migrations, db_name)
for ext in get_valid_extensions():
try:
- ext_migrations = importlib.import_module(
- f"lnbits.extensions.{ext.code}.migrations"
+
+ module_str = (
+ ext.migration_module or f"lnbits.extensions.{ext.code}.migrations"
)
+ ext_migrations = importlib.import_module(module_str)
ext_db = importlib.import_module(f"lnbits.extensions.{ext.code}").db
+ db_name = ext.db_name or module_str.split(".")[-2]
except ImportError:
raise ImportError(
f"Please make sure that the extension `{ext.code}` has a migrations file."
)
async with ext_db.connect() as ext_conn:
- await run_migration(ext_conn, ext_migrations)
+ await run_migration(ext_conn, ext_migrations, db_name)
logger.info("✔️ All migrations done.")
diff --git a/lnbits/extensions/cashu/ledger.py b/lnbits/extensions/cashu/ledger.py
deleted file mode 100644
index a28dc97a..00000000
--- a/lnbits/extensions/cashu/ledger.py
+++ /dev/null
@@ -1,351 +0,0 @@
-import hashlib
-from typing import List, Set
-
-from fastapi import Query
-from secp256k1 import PrivateKey, PublicKey
-
-from lnbits.core.services import check_transaction_status, create_invoice
-
-from .crud import get_cashu
-from .models import BlindedMessage, BlindedSignature, Invoice, Proof
-
-
-def _derive_keys(master_key: str, cashu_id: str = Query(None)):
- """Deterministic derivation of keys for 2^n values."""
- return {
- 2
- ** i: PrivateKey(
- hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
- .hexdigest()
- .encode("utf-8")[:32],
- raw=True,
- )
- for i in range(MAX_ORDER)
- }
-
-
-def _derive_pubkeys(keys: List[PrivateKey], cashu_id: str = Query(None)):
- return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
-
-
-async def _generate_promises(
- amounts: List[int], B_s: List[str], cashu_id: str = Query(None)
-):
- """Generates promises that sum to the given amount."""
- return [
- await self._generate_promise(amount, PublicKey(bytes.fromhex(B_), raw=True))
- for (amount, B_) in zip(amounts, B_s)
- ]
-
-
-async def _generate_promise(amount: int, B_: PublicKey, cashu_id: str = Query(None)):
- """Generates a promise for given amount and returns a pair (amount, C')."""
- secret_key = self.keys[amount] # Get the correct key
- C_ = step2_bob(B_, secret_key)
- await store_promise(amount, B_=B_.serialize().hex(), C_=C_.serialize().hex())
- return BlindedSignature(amount=amount, C_=C_.serialize().hex())
-
-
-def _check_spendable(proof: Proof, cashu_id: str = Query(None)):
- """Checks whether the proof was already spent."""
- return not proof.secret in self.proofs_used
-
-
-def _verify_proof(proof: Proof, cashu_id: str = Query(None)):
- """Verifies that the proof of promise was issued by this ledger."""
- if not self._check_spendable(proof):
- raise Exception(f"tokens already spent. Secret: {proof.secret}")
- secret_key = self.keys[proof.amount] # Get the correct key to check against
- C = PublicKey(bytes.fromhex(proof.C), raw=True)
- return verify(secret_key, C, proof.secret)
-
-
-def _verify_outputs(
- total: int,
- amount: int,
- output_data: List[BlindedMessage],
- cashu_id: str = Query(None),
-):
- """Verifies the expected split was correctly computed"""
- fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
- fst_outputs = amount_split(fst_amt)
- snd_outputs = amount_split(snd_amt)
- expected = fst_outputs + snd_outputs
- given = [o.amount for o in output_data]
- return given == expected
-
-
-def _verify_no_duplicates(
- proofs: List[Proof], output_data: List[BlindedMessage], cashu_id: str = Query(None)
-):
- secrets = [p.secret for p in proofs]
- if len(secrets) != len(list(set(secrets))):
- return False
- B_s = [od.B_ for od in output_data]
- if len(B_s) != len(list(set(B_s))):
- return False
- return True
-
-
-def _verify_split_amount(amount: int, cashu_id: str = Query(None)):
- """Split amount like output amount can't be negative or too big."""
- try:
- self._verify_amount(amount)
- except:
- # For better error message
- raise Exception("invalid split amount: " + str(amount))
-
-
-def _verify_amount(amount: int, cashu_id: str = Query(None)):
- """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
- valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
- if not valid:
- raise Exception("invalid amount: " + str(amount))
- return amount
-
-
-def _verify_equation_balanced(
- proofs: List[Proof], outs: List[BlindedMessage], cashu_id: str = Query(None)
-):
- """Verify that Σoutputs - Σinputs = 0."""
- sum_inputs = sum(self._verify_amount(p.amount) for p in proofs)
- sum_outputs = sum(self._verify_amount(p.amount) for p in outs)
- assert sum_outputs - sum_inputs == 0
-
-
-def _get_output_split(amount: int, cashu_id: str):
- """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
- self._verify_amount(amount)
- bits_amt = bin(amount)[::-1][:-2]
- rv = []
- for (pos, bit) in enumerate(bits_amt):
- if bit == "1":
- rv.append(2**pos)
- return rv
-
-
-async def _invalidate_proofs(proofs: List[Proof], cashu_id: str = Query(None)):
- """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
- # Mark proofs as used and prepare new promises
- proof_msgs = set([p.secret for p in proofs])
- self.proofs_used |= proof_msgs
- # store in db
- for p in proofs:
- await invalidate_proof(p)
-
-
-def get_pubkeys(cashu_id: str = Query(None)):
- """Returns public keys for possible amounts."""
- return {a: p.serialize().hex() for a, p in self.pub_keys.items()}
-
-
-async def request_mint(amount, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Returns Lightning invoice and stores it in the db."""
- payment_hash, payment_request = await create_invoice(
- wallet_id=cashu.wallet,
- amount=amount,
- memo=cashu.name,
- unhashed_description=cashu.name.encode("utf-8"),
- extra={"tag": "Cashu"},
- )
-
- invoice = Invoice(
- amount=amount, pr=payment_request, hash=payment_hash, issued=False
- )
- if not payment_request or not payment_hash:
- raise Exception(f"Could not create Lightning invoice.")
- return payment_request, payment_hash
-
-
-async def mint(
- B_s: List[PublicKey],
- amounts: List[int],
- payment_hash: str = Query(None),
- cashu_id: str = Query(None),
-):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Mints a promise for coins for B_."""
- # check if lightning invoice was paid
- if payment_hash:
- if not await check_transaction_status(
- wallet_id=cashu.wallet, payment_hash=payment_hash
- ):
- raise Exception("Lightning invoice not paid yet.")
-
- for amount in amounts:
- if amount not in [2**i for i in range(MAX_ORDER)]:
- raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
-
- promises = [
- await self._generate_promise(amount, B_) for B_, amount in zip(B_s, amounts)
- ]
- return promises
-
-
-async def melt(
- proofs: List[Proof], amount: int, invoice: str, cashu_id: str = Query(None)
-):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Invalidates proofs and pays a Lightning invoice."""
- # if not LIGHTNING:
- total = sum([p["amount"] for p in proofs])
- # check that lightning fees are included
- assert total + fee_reserve(amount * 1000) >= amount, Exception(
- "provided proofs not enough for Lightning payment."
- )
-
- status, payment_hash = await pay_invoice(
- wallet_id=link.wallet,
- payment_request=invoice,
- max_sat=amount,
- extra={"tag": "Ecash melt"},
- )
-
- if status == True:
- await self._invalidate_proofs(proofs)
- return status, payment_hash
-
-
-async def check_spendable(proofs: List[Proof], cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Checks if all provided proofs are valid and still spendable (i.e. have not been spent)."""
- return {i: self._check_spendable(p) for i, p in enumerate(proofs)}
-
-
-async def split(
- proofs: List[Proof],
- amount: int,
- output_data: List[BlindedMessage],
- cashu_id: str = Query(None),
-):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Consumes proofs and prepares new promises based on the amount split."""
- self._verify_split_amount(amount)
- # Verify proofs are valid
- if not all([self._verify_proof(p) for p in proofs]):
- return False
-
- total = sum([p.amount for p in proofs])
-
- if not self._verify_no_duplicates(proofs, output_data):
- raise Exception("duplicate proofs or promises")
- if amount > total:
- raise Exception("split amount is higher than the total sum")
- if not self._verify_outputs(total, amount, output_data):
- raise Exception("split of promises is not as expected")
-
- # Mark proofs as used and prepare new promises
- await self._invalidate_proofs(proofs)
-
- outs_fst = amount_split(total - amount)
- outs_snd = amount_split(amount)
- B_fst = [od.B_ for od in output_data[: len(outs_fst)]]
- B_snd = [od.B_ for od in output_data[len(outs_fst) :]]
- prom_fst, prom_snd = await self._generate_promises(
- outs_fst, B_fst
- ), await self._generate_promises(outs_snd, B_snd)
- self._verify_equation_balanced(proofs, prom_fst + prom_snd)
- return prom_fst, prom_snd
-
-
-async def fee_reserve(amount_msat: int, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Function for calculating the Lightning fee reserve"""
- return max(
- int(LIGHTNING_RESERVE_FEE_MIN), int(amount_msat * LIGHTNING_FEE_PERCENT / 100.0)
- )
-
-
-async def amount_split(amount, cashu_id: str):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
- bits_amt = bin(amount)[::-1][:-2]
- rv = []
- for (pos, bit) in enumerate(bits_amt):
- if bit == "1":
- rv.append(2**pos)
- return rv
-
-
-async def hash_to_point(secret_msg, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- """Generates x coordinate from the message hash and checks if the point lies on the curve.
- If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
- point = None
- msg = secret_msg
- while point is None:
- _hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
- try:
- # We construct compressed pub which has x coordinate encoded with even y
- _hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
- _hash[0] = 0x02 # set first byte to represent even y coord
- _hash = bytes(_hash)
- point = PublicKey(_hash, raw=True)
- except:
- msg = _hash
-
- return point
-
-
-async def step1_alice(secret_msg, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- secret_msg = secret_msg.encode("utf-8")
- Y = hash_to_point(secret_msg)
- r = PrivateKey()
- B_ = Y + r.pubkey
- return B_, r
-
-
-async def step2_bob(B_, a, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- C_ = B_.mult(a)
- return C_
-
-
-async def step3_alice(C_, r, A, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- C = C_ - A.mult(r)
- return C
-
-
-async def verify(a, C, secret_msg, cashu_id: str = Query(None)):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise Exception(f"Could not find Cashu")
-
- Y = hash_to_point(secret_msg.encode("utf-8"))
- return C == Y.mult(a)
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e213240c..81480b2b 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -20,6 +20,8 @@ class Extension(NamedTuple):
icon: Optional[str] = None
contributors: Optional[List[str]] = None
hidden: bool = False
+ migration_module: Optional[str] = None
+ db_name: Optional[str] = None
class ExtensionManager:
@@ -66,6 +68,8 @@ class ExtensionManager:
config.get("icon"),
config.get("contributors"),
config.get("hidden") or False,
+ config.get("migration_module"),
+ config.get("db_name"),
)
)
From 36907d8d7e71a1025f0cc67c223dfc2fabf7c929 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 13 Oct 2022 00:24:15 +0200
Subject: [PATCH 0180/1058] allow for internal and external migrations
---
lnbits/extensions/cashu/__init__.py | 4 +-
lnbits/extensions/cashu/config.json | 4 +-
lnbits/extensions/cashu/crud.py | 145 ++++++++++++-------------
lnbits/extensions/cashu/migrations.py | 149 +++++++++++++++++++++++++-
lnbits/extensions/cashu/tasks.py | 6 +-
lnbits/extensions/cashu/views_api.py | 7 ++
6 files changed, 234 insertions(+), 81 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index b36e94ef..81b18bc7 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -13,7 +13,8 @@ import sys
sys.path.append("/Users/cc/git/cashu")
from cashu.mint.ledger import Ledger
-from .crud import LedgerCrud
+
+# from .crud import LedgerCrud
# db = Database("ext_cashu", LNBITS_DATA_FOLDER)
@@ -22,7 +23,6 @@ ledger = Ledger(
# seed=MINT_PRIVATE_KEY,
seed="asd",
derivation_path="0/0/0/1",
- crud=LedgerCrud,
)
cashu_ext: APIRouter = APIRouter(prefix="/api/v1/cashu", tags=["cashu"])
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index e71e6889..99838eb4 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -3,7 +3,5 @@
"short_description": "Ecash mint and wallet",
"icon": "approval",
"contributors": ["arcbtc", "calle", "vlad"],
- "hidden": false,
- "migration_module": "cashu.mint.migrations",
- "db_name": "cashu"
+ "hidden": false
}
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 48b90d70..31c185dc 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -11,6 +11,7 @@ from loguru import logger
from lnbits.helpers import urlsafe_short_hash
from . import db
+
from .core.base import Invoice
from .models import Cashu, Pegs, Promises, Proof
@@ -18,44 +19,44 @@ from cashu.core.base import MintKeyset
from lnbits.db import Database, Connection
-class LedgerCrud:
- """
- Database interface for Cashu mint.
+# class LedgerCrud:
+# """
+# Database interface for Cashu mint.
- This class needs to be overloaded by any app that imports the Cashu mint.
- """
+# This class needs to be overloaded by any app that imports the Cashu mint.
+# """
- async def get_keyset(*args, **kwags):
+# async def get_keyset(*args, **kwags):
- return await get_keyset(*args, **kwags)
+# return await get_keyset(*args, **kwags)
- async def get_lightning_invoice(*args, **kwags):
+# async def get_lightning_invoice(*args, **kwags):
- return await get_lightning_invoice(*args, **kwags)
+# return await get_lightning_invoice(*args, **kwags)
- async def get_proofs_used(*args, **kwags):
+# async def get_proofs_used(*args, **kwags):
- return await get_proofs_used(*args, **kwags)
+# return await get_proofs_used(*args, **kwags)
- async def invalidate_proof(*args, **kwags):
+# async def invalidate_proof(*args, **kwags):
- return await invalidate_proof(*args, **kwags)
+# return await invalidate_proof(*args, **kwags)
- async def store_keyset(*args, **kwags):
+# async def store_keyset(*args, **kwags):
- return await store_keyset(*args, **kwags)
+# return await store_keyset(*args, **kwags)
- async def store_lightning_invoice(*args, **kwags):
+# async def store_lightning_invoice(*args, **kwags):
- return await store_lightning_invoice(*args, **kwags)
+# return await store_lightning_invoice(*args, **kwags)
- async def store_promise(*args, **kwags):
+# async def store_promise(*args, **kwags):
- return await store_promise(*args, **kwags)
+# return await store_promise(*args, **kwags)
- async def update_lightning_invoice(*args, **kwags):
+# async def update_lightning_invoice(*args, **kwags):
- return await update_lightning_invoice(*args, **kwags)
+# return await update_lightning_invoice(*args, **kwags)
async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
@@ -132,9 +133,9 @@ async def delete_cashu(cashu_id) -> None:
await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,))
-##########################################
-###############MINT STUFF#################
-##########################################
+# ##########################################
+# ###############MINT STUFF#################
+# ##########################################
async def store_promises(
@@ -241,55 +242,55 @@ async def update_lightning_invoice(cashu_id: str, hash: str, issued: bool):
##############################
-async def store_keyset(
- keyset: MintKeyset,
- db: Database = None,
- conn: Optional[Connection] = None,
-):
+# 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,
- ),
- )
+# 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)}"
+# 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]
+# 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 019ee84d..6af53d5d 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -1 +1,148 @@
-# this extension will use the migration_module module cashu.mint.migrations (see config.json)
+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
+ );
+ """
+ )
+
+
+# async def m001_initial(db):
+# await db.execute(
+# """
+# CREATE TABLE IF NOT EXISTS cashu.promises (
+# amount INTEGER NOT NULL,
+# B_b TEXT NOT NULL,
+# C_b TEXT NOT NULL,
+
+# UNIQUE (B_b)
+
+# );
+# """
+# )
+
+# await db.execute(
+# """
+# CREATE TABLE IF NOT EXISTS cashu.proofs_used (
+# amount INTEGER NOT NULL,
+# C TEXT NOT NULL,
+# secret TEXT NOT NULL,
+
+# UNIQUE (secret)
+
+# );
+# """
+# )
+
+# await db.execute(
+# """
+# CREATE TABLE IF NOT EXISTS cashu.invoices (
+# amount INTEGER NOT NULL,
+# pr TEXT NOT NULL,
+# hash TEXT NOT NULL,
+# issued BOOL NOT NULL,
+
+# UNIQUE (hash)
+
+# );
+# """
+# )
+
+# await db.execute(
+# """
+# CREATE VIEW IF NOT EXISTS cashu.balance_issued AS
+# SELECT COALESCE(SUM(s), 0) AS balance FROM (
+# SELECT SUM(amount) AS s
+# FROM cashu.promises
+# WHERE amount > 0
+# );
+# """
+# )
+
+# await db.execute(
+# """
+# CREATE VIEW IF NOT EXISTS cashu.balance_used AS
+# SELECT COALESCE(SUM(s), 0) AS balance FROM (
+# SELECT SUM(amount) AS s
+# FROM cashu.proofs_used
+# WHERE amount > 0
+# );
+# """
+# )
+
+# await db.execute(
+# """
+# CREATE VIEW IF NOT EXISTS cashu.balance AS
+# SELECT s_issued - s_used AS balance FROM (
+# SELECT bi.balance AS s_issued, bu.balance AS s_used
+# FROM cashu.balance_issued bi
+# CROSS JOIN balance_used bu
+# );
+# """
+# )
+
+
+# async def m003_mint_keysets(db):
+# """
+# Stores mint keysets from different mints and epochs.
+# """
+# await db.execute(
+# f"""
+# CREATE TABLE IF NOT EXISTS cashu.keysets (
+# id TEXT NOT NULL,
+# derivation_path TEXT,
+# valid_from TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
+# valid_to TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
+# first_seen TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
+# active BOOL DEFAULT TRUE,
+
+# UNIQUE (derivation_path)
+
+# );
+# """
+# )
+# await db.execute(
+# f"""
+# CREATE TABLE IF NOT EXISTS cashu.mint_pubkeys (
+# id TEXT NOT NULL,
+# amount INTEGER NOT NULL,
+# pubkey TEXT NOT NULL,
+
+# UNIQUE (id, pubkey)
+
+# );
+# """
+# )
+
+
+# async def m004_keysets_add_version(db):
+# """
+# Column that remembers with which version
+# """
+# await db.execute("ALTER TABLE cashu.keysets ADD COLUMN version TEXT")
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
index 9d2d7f15..03bba895 100644
--- a/lnbits/extensions/cashu/tasks.py
+++ b/lnbits/extensions/cashu/tasks.py
@@ -12,13 +12,13 @@ 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 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 migrate_databases(db, migrations)
await ledger.load_used_proofs()
await ledger.init_keysets()
print(ledger.get_keyset())
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index e786cd2f..e643c2ac 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -29,6 +29,13 @@ from .crud import (
update_lightning_invoice,
)
+# from cashu.mint.crud import (
+# get_lightning_invoice,
+# store_lightning_invoice,
+# store_promise,
+# update_lightning_invoice,
+# )
+
# from .ledger import mint, request_mint
from .mint import generate_promises, get_pubkeys, melt, split
from .models import (
From dde28e66a24e09517b668e291ed58a6eb7df37e2 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 13 Oct 2022 15:52:02 +0100
Subject: [PATCH 0181/1058] added tooltips, moved reset, and warnings
---
.../admin/templates/admin/index.html | 97 ++++++++++++-------
1 file changed, 61 insertions(+), 36 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 575b377f..7d268301 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1,8 +1,14 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
-
-
+
+
+ Save your changes
-
-
-
+
+
+ Restart the server for changes to take effect
+
+
+
+
+ Add funds to a wallet.
+
+ > -->
+
+ Delete all settings and reset to defaults.
+
@@ -121,6 +136,7 @@
show: false
},
tab: 'funding',
+ needsRestart: false,
funding_sources: new Map([
['VoidWallet', null],
[
@@ -302,13 +318,12 @@
this.balance = +'{{ balance|safe }}'
this.formData = _.clone(this.settings) //model
this.updateFundingData()
-
console.log(this.settings)
},
computed: {
checkChanges() {
return !_.isEqual(this.settings, this.formData)
- },
+ }
},
methods: {
addAdminUser() {
@@ -361,6 +376,7 @@
message: 'Success! Restarted Server',
icon: null
})
+ this.needsRestart = false
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -390,16 +406,15 @@
LNbits.utils.notifyApiError(error)
})
},
- updateFundingData(){
+ updateFundingData() {
this.settings.lnbits_allowed_funding_sources.map(f => {
let opts = this.funding_sources.get(f)
if (!opts) return
-
+
Object.keys(opts).forEach(e => {
opts[e].value = this.settings[e]
})
})
- console.log("funding", this.funding_sources)
},
updateSettings() {
let data = {
@@ -415,31 +430,41 @@
.then(response => {
this.settings = response.data.settings
this.formData = _.clone(this.settings)
+ this.needsRestart = true
this.updateFundingData()
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
icon: null
})
- console.log(this.settings)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteSettings() {
- LNbits.api
- .request('DELETE', '/admin/api/v1/settings/?usr=' + this.g.user.id)
- .then(response => {
- this.$q.notify({
- type: 'positive',
- message:
- 'Success! Restored settings to defaults, restart required!',
- icon: null
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
+ LNbits.utils
+ .confirmDialog(
+ 'Are you sure you want to restore settings to default?'
+ )
+ .onOk(() => {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/admin/api/v1/settings/?usr=' + this.g.user.id
+ )
+ .then(response => {
+ this.$q.notify({
+ type: 'positive',
+ message:
+ 'Success! Restored settings to defaults, restart required!',
+ icon: null
+ })
+ this.needsRestart = true
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
})
},
downloadBackup() {
From fb2dee73955aef2700bdf52decb6a570440beb23 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 13 Oct 2022 15:52:26 +0100
Subject: [PATCH 0182/1058] moved to columns
---
.../admin/templates/admin/_tab_funding.html | 54 +++++++++----------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index a523d4e5..a69ecb47 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -27,38 +27,38 @@
:options="settings.lnbits_allowed_funding_sources"
>
-
+
-
-
-
- Funding Sources (Requires server restart)
+
+ Funding Sources (Requires server restart)
+
Date: Thu, 13 Oct 2022 15:52:39 +0100
Subject: [PATCH 0183/1058] themes not displaying fixed
---
lnbits/extensions/admin/templates/admin/_tab_theme.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html
index 46bf83e9..c327733f 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_theme.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html
@@ -63,7 +63,7 @@
Themes
Date: Thu, 13 Oct 2022 22:10:33 +0200
Subject: [PATCH 0184/1058] endpoints work
---
lnbits/extensions/cashu/crud.py | 20 +-
lnbits/extensions/cashu/migrations.py | 3 +-
lnbits/extensions/cashu/models.py | 4 +-
lnbits/extensions/cashu/views_api.py | 675 +++++++++++++-------------
poetry.lock | 17 +-
pyproject.toml | 1 +
6 files changed, 372 insertions(+), 348 deletions(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 31c185dc..a56eadf0 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -59,21 +59,14 @@ from lnbits.db import Database, Connection
# return await update_lightning_invoice(*args, **kwags)
-async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
- cashu_id = urlsafe_short_hash()
-
- entropy = bytes([random.getrandbits(8) for i in range(16)])
- mnemonic = bip39.mnemonic_from_bytes(entropy)
- seed = bip39.mnemonic_to_seed(mnemonic)
- root = bip32.HDKey.from_seed(seed, version=NETWORKS["main"]["xprv"])
-
- bip44_xprv = root.derive("m/44h/1h/0h")
- bip44_xpub = bip44_xprv.to_public()
+async def create_cashu(
+ cashu_id: str, keyset_id: str, wallet_id: str, data: Cashu
+) -> Cashu:
await db.execute(
"""
- INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, prvkey, pubkey)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, keyset_id)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
cashu_id,
@@ -83,8 +76,7 @@ async def create_cashu(wallet_id: str, data: Cashu) -> Cashu:
data.fraction,
data.maxsats,
data.coins,
- bip44_xprv.to_base58(),
- bip44_xpub.to_base58(),
+ keyset_id,
),
)
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index 6af53d5d..3f799534 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -12,8 +12,7 @@ async def m001_initial(db):
fraction BOOL,
maxsats INT,
coins INT,
- prvkey TEXT NOT NULL,
- pubkey TEXT NOT NULL
+ keyset_id TEXT NOT NULL
);
"""
)
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 596db047..49fa9059 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -17,7 +17,7 @@ class Cashu(BaseModel):
pubkey: str = Query(None)
@classmethod
- def from_row(cls, row: Row) -> "TPoS":
+ def from_row(cls, row: Row):
return cls(**dict(row))
@@ -28,7 +28,7 @@ class Pegs(BaseModel):
amount: str
@classmethod
- def from_row(cls, row: Row) -> "TPoS":
+ def from_row(cls, row: Row):
return cls(**dict(row))
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index e643c2ac..f47f8744 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -15,6 +15,7 @@ from lnbits.core.services import check_transaction_status, create_invoice
from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.wallets.base import PaymentStatus
+from lnbits.helpers import urlsafe_short_hash
from . import cashu_ext
from .core.base import CashuError, PostSplitResponse, SplitRequest
@@ -29,13 +30,6 @@ from .crud import (
update_lightning_invoice,
)
-# from cashu.mint.crud import (
-# get_lightning_invoice,
-# store_lightning_invoice,
-# store_promise,
-# update_lightning_invoice,
-# )
-
# from .ledger import mint, request_mint
from .mint import generate_promises, get_pubkeys, melt, split
from .models import (
@@ -49,327 +43,8 @@ from .models import (
SplitPayload,
)
-########################################
-#################MINT CRUD##############
-########################################
-
-# todo: use /mints
-@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
-async def api_cashus(
- all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
-):
- wallet_ids = [wallet.wallet.id]
- if all_wallets:
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
-
- return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
-
-
-@cashu_ext.post("/api/v1/cashus", status_code=HTTPStatus.CREATED)
-async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)):
- cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
- logger.debug(cashu)
- return cashu.dict()
-
-
-@cashu_ext.post("/api/v1/cashus/upodatekeys", status_code=HTTPStatus.CREATED)
-async def api_cashu_update_keys(
- data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
-):
- cashu = await get_cashu(data.id)
-
- cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
- logger.debug(cashu)
- return cashu.dict()
-
-
-@cashu_ext.delete("/api/v1/cashus/{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)
-
-
-########################################
-#################????###################
-########################################
-@cashu_ext.post("/api/v1/cashus/{cashu_id}/invoices", status_code=HTTPStatus.CREATED)
-async def api_cashu_create_invoice(
- amount: int = Query(..., ge=1), tipAmount: int = None, cashu_id: str = None
-):
- cashu = await get_cashu(cashu_id)
-
- if not cashu:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
- )
-
- if tipAmount:
- amount += tipAmount
-
- try:
- payment_hash, payment_request = await create_invoice(
- wallet_id=cashu.wallet,
- amount=amount,
- memo=f"{cashu.name}",
- extra={"tag": "cashu", "tipAmount": tipAmount, "cashuId": cashu_id},
- )
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-
- return {"payment_hash": payment_hash, "payment_request": payment_request}
-
-
-@cashu_ext.post(
- "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay",
- status_code=HTTPStatus.OK,
-)
-async def api_cashu_pay_invoice(
- lnurl_data: PayLnurlWData, payment_request: str = None, cashu_id: str = None
-):
- cashu = await get_cashu(cashu_id)
-
- if not cashu:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
- )
-
- lnurl = (
- lnurl_data.lnurl.replace("lnurlw://", "")
- .replace("lightning://", "")
- .replace("LIGHTNING://", "")
- .replace("lightning:", "")
- .replace("LIGHTNING:", "")
- )
-
- if lnurl.lower().startswith("lnurl"):
- lnurl = decode_lnurl(lnurl)
- else:
- lnurl = "https://" + lnurl
-
- async with httpx.AsyncClient() as client:
- try:
- r = await client.get(lnurl, follow_redirects=True)
- if r.is_error:
- lnurl_response = {"success": False, "detail": "Error loading"}
- else:
- resp = r.json()
- if resp["tag"] != "withdrawRequest":
- lnurl_response = {"success": False, "detail": "Wrong tag type"}
- else:
- r2 = await client.get(
- resp["callback"],
- follow_redirects=True,
- params={
- "k1": resp["k1"],
- "pr": payment_request,
- },
- )
- resp2 = r2.json()
- if r2.is_error:
- lnurl_response = {
- "success": False,
- "detail": "Error loading callback",
- }
- elif resp2["status"] == "ERROR":
- lnurl_response = {"success": False, "detail": resp2["reason"]}
- else:
- lnurl_response = {"success": True, "detail": resp2}
- except (httpx.ConnectError, httpx.RequestError):
- lnurl_response = {"success": False, "detail": "Unexpected error occurred"}
-
- return lnurl_response
-
-
-@cashu_ext.get(
- "/api/v1/cashus/{cashu_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
-)
-async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
- cashu = await get_cashu(cashu_id)
- if not cashu:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
- )
- try:
- status = await api_payment(payment_hash)
-
- except Exception as exc:
- logger.error(exc)
- return {"paid": False}
- return status
-
-
-########################################
-#################MINT###################
-########################################
-
-
-# @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")
-async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
- """Request minting of tokens. Server responds with a Lightning invoice."""
-
- cashu = await get_cashu(cashu_id)
- if cashu is None:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
- )
-
- 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)
- except Exception as e:
- logger.error(e)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-
- return {"pr": payment_request, "hash": payment_hash}
-
-
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
-async def mint_coins(
- data: MintPayloads,
- cashu_id: str = Query(None),
- payment_hash: Union[str, None] = None,
-):
- """
- Requests the minting of tokens belonging to a paid payment request.
- Call this endpoint after `GET /mint`.
- """
- cashu: Cashu = await get_cashu(cashu_id)
- if cashu is None:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
- )
- invoice: Invoice = (
- None
- if payment_hash == None
- else await get_lightning_invoice(cashu_id, 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:
- await update_lightning_invoice(cashu_id, payment_hash, True)
-
- amounts = []
- B_s = []
- for payload in data.blinded_messages:
- amounts.append(payload.amount)
- B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
-
- promises = await generate_promises(cashu.prvkey, amounts, B_s)
- for amount, B_, p in zip(amounts, B_s, promises):
- await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
-
- return promises
- except Exception as e:
- logger.error(e)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-
-
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/melt")
-async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
- """Invalidates proofs and pays a Lightning invoice."""
- cashu: Cashu = await get_cashu(cashu_id)
- if cashu is None:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
- )
- try:
- ok, preimage = await melt(cashu, payload.proofs, payload.invoice)
- return {"paid": ok, "preimage": preimage}
- except Exception as e:
- logger.error(e)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-
-
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/check")
-async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(None)):
- return await check_spendable(payload.proofs, cashu_id)
-
-
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
-async def split_proofs(payload: SplitRequest, cashu_id: str = Query(None)):
- """
- Requetst a set of tokens with amount "total" to be split into two
- newly minted sets with amount "split" and "total-split".
- """
- print("### RECEIVE")
- print("payload", json.dumps(payload, default=vars))
- cashu: 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
- amount = payload.amount
- outputs = payload.outputs.blinded_messages if payload.outputs else None
- try:
- split_return = await split(cashu, proofs, amount, outputs)
- except Exception as exc:
- raise CashuError(error=str(exc))
- if not split_return:
- return {"error": "there was a problem with the split."}
- frst_promises, scnd_promises = split_return
- resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
- print("### resp", json.dumps(resp, default=vars))
- return resp
-
-
-##################################################################
-##################################################################
-# CASHU LIB
-##################################################################
+############### IMPORT CALLE
from typing import Dict, List, Union
from fastapi import APIRouter
@@ -389,11 +64,54 @@ from cashu.core.base import (
)
from cashu.core.errors import CashuError
+
+########################################
+############### LNBITS MINTS ###########
+########################################
+
+# todo: use /mints
+@cashu_ext.get("/cashus", status_code=HTTPStatus.OK)
+async def api_cashus(
+ all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
+):
+ wallet_ids = [wallet.wallet.id]
+ if all_wallets:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
+
+
+@cashu_ext.post("/cashus", status_code=HTTPStatus.CREATED)
+async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)):
+ cashu_id = urlsafe_short_hash()
+ # generate a new keyset in cashu
+ keyset = await ledger.load_keyset(cashu_id)
+
+ cashu = await create_cashu(
+ cashu_id=cashu_id, keyset_id=keyset.id, wallet_id=wallet.wallet.id, data=data
+ )
+ logger.debug(cashu)
+ return cashu.dict()
+
+
+#######################################
+########### CASHU ENDPOINTS ###########
+#######################################
+
+
from . import db, ledger
-@cashu_ext.get("/keys")
-async def keys() -> dict[int, str]:
+@cashu_ext.get("{cashu_id}/keys", status_code=HTTPStatus.OK)
+async def keys(cashu_id: str = None) -> dict[int, str]:
+
+ cashu = await get_cashu(cashu_id)
+
+ if not cashu:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+ )
+
"""Get the public keys of the mint"""
return ledger.get_keyset()
@@ -489,3 +207,302 @@ async def split(
frst_promises, scnd_promises = split_return
resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
return resp
+
+
+# @cashu_ext.post("/api/v1/cashus/upodatekeys", status_code=HTTPStatus.CREATED)
+# async def api_cashu_update_keys(
+# data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
+# ):
+# cashu = await get_cashu(data.id)
+
+# cashu = await create_cashu(wallet_id=wallet.wallet.id, data=data)
+# logger.debug(cashu)
+# return cashu.dict()
+
+
+# @cashu_ext.delete("/api/v1/cashus/{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)
+
+
+# ########################################
+# #################????###################
+# ########################################
+# @cashu_ext.post("/api/v1/cashus/{cashu_id}/invoices", status_code=HTTPStatus.CREATED)
+# async def api_cashu_create_invoice(
+# amount: int = Query(..., ge=1), tipAmount: int = None, cashu_id: str = None
+# ):
+# cashu = await get_cashu(cashu_id)
+
+# if not cashu:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+# )
+
+# if tipAmount:
+# amount += tipAmount
+
+# try:
+# payment_hash, payment_request = await create_invoice(
+# wallet_id=cashu.wallet,
+# amount=amount,
+# memo=f"{cashu.name}",
+# extra={"tag": "cashu", "tipAmount": tipAmount, "cashuId": cashu_id},
+# )
+# except Exception as e:
+# raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+# return {"payment_hash": payment_hash, "payment_request": payment_request}
+
+
+# @cashu_ext.post(
+# "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay",
+# status_code=HTTPStatus.OK,
+# )
+# async def api_cashu_pay_invoice(
+# lnurl_data: PayLnurlWData, payment_request: str = None, cashu_id: str = None
+# ):
+# cashu = await get_cashu(cashu_id)
+
+# if not cashu:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+# )
+
+# lnurl = (
+# lnurl_data.lnurl.replace("lnurlw://", "")
+# .replace("lightning://", "")
+# .replace("LIGHTNING://", "")
+# .replace("lightning:", "")
+# .replace("LIGHTNING:", "")
+# )
+
+# if lnurl.lower().startswith("lnurl"):
+# lnurl = decode_lnurl(lnurl)
+# else:
+# lnurl = "https://" + lnurl
+
+# async with httpx.AsyncClient() as client:
+# try:
+# r = await client.get(lnurl, follow_redirects=True)
+# if r.is_error:
+# lnurl_response = {"success": False, "detail": "Error loading"}
+# else:
+# resp = r.json()
+# if resp["tag"] != "withdrawRequest":
+# lnurl_response = {"success": False, "detail": "Wrong tag type"}
+# else:
+# r2 = await client.get(
+# resp["callback"],
+# follow_redirects=True,
+# params={
+# "k1": resp["k1"],
+# "pr": payment_request,
+# },
+# )
+# resp2 = r2.json()
+# if r2.is_error:
+# lnurl_response = {
+# "success": False,
+# "detail": "Error loading callback",
+# }
+# elif resp2["status"] == "ERROR":
+# lnurl_response = {"success": False, "detail": resp2["reason"]}
+# else:
+# lnurl_response = {"success": True, "detail": resp2}
+# except (httpx.ConnectError, httpx.RequestError):
+# lnurl_response = {"success": False, "detail": "Unexpected error occurred"}
+
+# return lnurl_response
+
+
+# @cashu_ext.get(
+# "/api/v1/cashus/{cashu_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
+# )
+# async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
+# cashu = await get_cashu(cashu_id)
+# if not cashu:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
+# )
+# try:
+# status = await api_payment(payment_hash)
+
+# except Exception as exc:
+# logger.error(exc)
+# return {"paid": False}
+# return status
+
+
+# ########################################
+# #################MINT###################
+# ########################################
+
+
+# # @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")
+# async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
+# """Request minting of tokens. Server responds with a Lightning invoice."""
+
+# cashu = await get_cashu(cashu_id)
+# if cashu is None:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+# )
+
+# 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)
+# except Exception as e:
+# logger.error(e)
+# raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+# return {"pr": payment_request, "hash": payment_hash}
+
+
+# @cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
+# async def mint_coins(
+# data: MintPayloads,
+# cashu_id: str = Query(None),
+# payment_hash: Union[str, None] = None,
+# ):
+# """
+# Requests the minting of tokens belonging to a paid payment request.
+# Call this endpoint after `GET /mint`.
+# """
+# cashu: Cashu = await get_cashu(cashu_id)
+# if cashu is None:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+# )
+# invoice: Invoice = (
+# None
+# if payment_hash == None
+# else await get_lightning_invoice(cashu_id, 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:
+# await update_lightning_invoice(cashu_id, payment_hash, True)
+
+# amounts = []
+# B_s = []
+# for payload in data.blinded_messages:
+# amounts.append(payload.amount)
+# B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
+
+# promises = await generate_promises(cashu.prvkey, amounts, B_s)
+# for amount, B_, p in zip(amounts, B_s, promises):
+# await store_promise(amount, B_.serialize().hex(), p.C_, cashu_id)
+
+# return promises
+# except Exception as e:
+# logger.error(e)
+# raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# @cashu_ext.post("/api/v1/cashu/{cashu_id}/melt")
+# async def melt_coins(payload: MeltPayload, cashu_id: str = Query(None)):
+# """Invalidates proofs and pays a Lightning invoice."""
+# cashu: Cashu = await get_cashu(cashu_id)
+# if cashu is None:
+# raise HTTPException(
+# status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+# )
+# try:
+# ok, preimage = await melt(cashu, payload.proofs, payload.invoice)
+# return {"paid": ok, "preimage": preimage}
+# except Exception as e:
+# logger.error(e)
+# raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# @cashu_ext.post("/api/v1/cashu/{cashu_id}/check")
+# async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(None)):
+# return await check_spendable(payload.proofs, cashu_id)
+
+
+# @cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
+# async def split_proofs(payload: SplitRequest, cashu_id: str = Query(None)):
+# """
+# Requetst a set of tokens with amount "total" to be split into two
+# newly minted sets with amount "split" and "total-split".
+# """
+# print("### RECEIVE")
+# print("payload", json.dumps(payload, default=vars))
+# cashu: 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
+# amount = payload.amount
+# outputs = payload.outputs.blinded_messages if payload.outputs else None
+# try:
+# split_return = await split(cashu, proofs, amount, outputs)
+# except Exception as exc:
+# raise CashuError(error=str(exc))
+# if not split_return:
+# return {"error": "there was a problem with the split."}
+# frst_promises, scnd_promises = split_return
+# resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
+# print("### resp", json.dumps(resp, default=vars))
+# return resp
+
+
+##################################################################
+##################################################################
+# CASHU LIB
+##################################################################
diff --git a/poetry.lock b/poetry.lock
index 5b283d75..3869f19b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -935,6 +935,17 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""
[package.extras]
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+[[package]]
+name = "starlette-context"
+version = "0.3.4"
+description = "Access context in Starlette"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+starlette = "*"
+
[[package]]
name = "tomli"
version = "2.0.1"
@@ -1051,7 +1062,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
+content-hash = "307f1d22c4cd0aecaac7b1bef917c260373f784f88538ff3f7f8cf45eb485372"
[metadata.files]
aiofiles = [
@@ -1865,6 +1876,10 @@ starlette = [
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
]
+starlette-context = [
+ {file = "starlette_context-0.3.4-py37-none-any.whl", hash = "sha256:b16bf17bd3ead7ded2f458aebf7f913744b9cf28305e16c69b435a6c6ddf1135"},
+ {file = "starlette_context-0.3.4.tar.gz", hash = "sha256:2d28e1838302fb5d5adacadc10fb73fb2d5cca1f0aa1e279698701cc96f1567c"},
+]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
diff --git a/pyproject.toml b/pyproject.toml
index 19dac860..bd1cdfd1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,6 +64,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
+starlette-context = "^0.3.4"
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From 436f208df27a4f64d3805566d14d1a8bc4d14233 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 14 Oct 2022 00:06:02 +0200
Subject: [PATCH 0185/1058] more endpoints working
---
lnbits/extensions/cashu/models.py | 3 +-
lnbits/extensions/cashu/views_api.py | 189 ++++++++++++++++++++++-----
2 files changed, 154 insertions(+), 38 deletions(-)
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
index 49fa9059..c820d12e 100644
--- a/lnbits/extensions/cashu/models.py
+++ b/lnbits/extensions/cashu/models.py
@@ -13,8 +13,7 @@ class Cashu(BaseModel):
fraction: bool = Query(None)
maxsats: int = Query(0)
coins: int = Query(0)
- prvkey: str = Query(None)
- pubkey: str = Query(None)
+ keyset_id: str = Query(None)
@classmethod
def from_row(cls, row: Row):
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index f47f8744..2d28c78b 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -1,6 +1,7 @@
import json
from http import HTTPStatus
from typing import Union
+import math
import httpx
from fastapi import Query
@@ -9,14 +10,21 @@ from lnurl import decode as decode_lnurl
from loguru import logger
from secp256k1 import PublicKey
from starlette.exceptions import HTTPException
+from lnbits import bolt11
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.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.wallets.base import PaymentStatus
from lnbits.helpers import urlsafe_short_hash
-
+from lnbits.core.crud import check_internal
from . import cashu_ext
from .core.base import CashuError, PostSplitResponse, SplitRequest
from .crud import (
@@ -47,10 +55,10 @@ from .models import (
############### IMPORT CALLE
from typing import Dict, List, Union
-from fastapi import APIRouter
from secp256k1 import PublicKey
from cashu.core.base import (
+ Proof,
BlindedSignature,
CheckFeesRequest,
CheckFeesResponse,
@@ -63,7 +71,9 @@ from cashu.core.base import (
SplitRequest,
)
from cashu.core.errors import CashuError
+from . import db, ledger
+LIGHTNING = False
########################################
############### 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 = None) -> dict[int, str]:
-
- cashu = await get_cashu(cashu_id)
+@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: Union[Cashu, None] = await get_cashu(cashu_id)
if not cashu:
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()
+ return ledger.get_keyset(keyset_id=cashu.keyset_id)
-@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:
+@cashu_ext.get("/{cashu_id}/mint")
+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.
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)
+ 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}")
resp = GetMintResponse(pr=payment_request, hash=payment_hash)
+ # return {"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]:
+@cashu_ext.post("/{cashu_id}/mint")
+async def mint_coins(
+ data: MintRequest,
+ cashu_id: str = Query(None),
+ payment_hash: str = Query(None),
+):
"""
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))
+ cashu: Union[Cashu, None] = await get_cashu(cashu_id)
+ if cashu is None:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
+ )
+
+ 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:
- 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
- except Exception as exc:
- return CashuError(error=str(exc))
+ except Exception as e:
+ 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")
From dafcfef5bef1d6400cdd297b83786507468a99dd Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 14 Oct 2022 01:01:43 +0200
Subject: [PATCH 0186/1058] cleanup
---
lnbits/extensions/cashu/__init__.py | 22 +--
lnbits/extensions/cashu/crud.py | 228 ++++++++++---------------
lnbits/extensions/cashu/migrations.py | 115 -------------
lnbits/extensions/cashu/mint.py | 155 -----------------
lnbits/extensions/cashu/mint_helper.py | 97 -----------
lnbits/extensions/cashu/tasks.py | 55 +-----
lnbits/extensions/cashu/views_api.py | 86 +++++-----
7 files changed, 141 insertions(+), 617 deletions(-)
delete mode 100644 lnbits/extensions/cashu/mint.py
delete mode 100644 lnbits/extensions/cashu/mint_helper.py
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index 81b18bc7..6e19eb6e 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -11,13 +11,16 @@ db = Database("ext_cashu")
import sys
+cashu_static_files = [
+ {
+ "path": "/cashu/static",
+ "app": StaticFiles(directory="lnbits/extensions/cashu/static"),
+ "name": "cashu_static",
+ }
+]
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,
@@ -26,17 +29,6 @@ ledger = Ledger(
)
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 = [
- {
- "path": "/cashu/static",
- "app": StaticFiles(directory="lnbits/extensions/cashu/static"),
- "name": "cashu_static",
- }
-]
def cashu_renderer():
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index a56eadf0..2057f6ff 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -19,46 +19,6 @@ 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(
cashu_id: str, keyset_id: str, wallet_id: str, data: Cashu
) -> Cashu:
@@ -85,23 +45,23 @@ async def create_cashu(
return cashu
-async def update_cashu_keys(cashu_id, wif: str = None) -> Optional[Cashu]:
- entropy = bytes([random.getrandbits(8) for i in range(16)])
- mnemonic = bip39.mnemonic_from_bytes(entropy)
- seed = bip39.mnemonic_to_seed(mnemonic)
- root = bip32.HDKey.from_seed(seed, version=NETWORKS["main"]["xprv"])
+# async def update_cashu_keys(cashu_id, wif: str = None) -> Optional[Cashu]:
+# entropy = bytes([random.getrandbits(8) for i in range(16)])
+# mnemonic = bip39.mnemonic_from_bytes(entropy)
+# seed = bip39.mnemonic_to_seed(mnemonic)
+# root = bip32.HDKey.from_seed(seed, version=NETWORKS["main"]["xprv"])
- bip44_xprv = root.derive("m/44h/1h/0h")
- bip44_xpub = bip44_xprv.to_public()
+# bip44_xprv = root.derive("m/44h/1h/0h")
+# bip44_xpub = bip44_xprv.to_public()
- await db.execute(
- "UPDATE cashu.cashu SET prv = ?, pub = ? WHERE id = ?",
- bip44_xprv.to_base58(),
- bip44_xpub.to_base58(),
- cashu_id,
- )
- row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
- return Cashu(**row) if row else None
+# await db.execute(
+# "UPDATE cashu.cashu SET prv = ?, pub = ? WHERE id = ?",
+# bip44_xprv.to_base58(),
+# bip44_xpub.to_base58(),
+# cashu_id,
+# )
+# row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
+# return Cashu(**row) if row else None
async def get_cashu(cashu_id) -> Optional[Cashu]:
@@ -130,103 +90,103 @@ async def delete_cashu(cashu_id) -> None:
# ##########################################
-async def store_promises(
- amounts: List[int], B_s: List[str], C_s: List[str], cashu_id: str
-):
- for amount, B_, C_ in zip(amounts, B_s, C_s):
- await store_promise(amount, B_, C_, cashu_id)
+# async def store_promises(
+# amounts: List[int], B_s: List[str], C_s: List[str], cashu_id: str
+# ):
+# for amount, B_, C_ in zip(amounts, B_s, C_s):
+# await store_promise(amount, B_, C_, cashu_id)
-async def store_promise(amount: int, B_: str, C_: str, cashu_id: str):
- promise_id = urlsafe_short_hash()
+# async def store_promise(amount: int, B_: str, C_: str, cashu_id: str):
+# promise_id = urlsafe_short_hash()
- await db.execute(
- """
- INSERT INTO cashu.promises
- (id, amount, B_b, C_b, cashu_id)
- VALUES (?, ?, ?, ?, ?)
- """,
- (promise_id, amount, str(B_), str(C_), cashu_id),
- )
+# await db.execute(
+# """
+# INSERT INTO cashu.promises
+# (id, amount, B_b, C_b, cashu_id)
+# VALUES (?, ?, ?, ?, ?)
+# """,
+# (promise_id, amount, str(B_), str(C_), cashu_id),
+# )
-async def get_promises(cashu_id) -> Optional[Cashu]:
- row = await db.fetchall(
- "SELECT * FROM cashu.promises WHERE cashu_id = ?", (cashu_id,)
- )
- return Promises(**row) if row else None
+# async def get_promises(cashu_id) -> Optional[Cashu]:
+# row = await db.fetchall(
+# "SELECT * FROM cashu.promises WHERE cashu_id = ?", (cashu_id,)
+# )
+# return Promises(**row) if row else None
-async def get_proofs_used(
- db: Database,
- conn: Optional[Connection] = None,
-):
+# 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]
+# rows = await (conn or db).fetchall(
+# """
+# SELECT secret from cashu.proofs_used
+# """
+# )
+# return [row[0] for row in rows]
-async def invalidate_proof(cashu_id: str, proof: Proof):
- invalidate_proof_id = urlsafe_short_hash()
- await db.execute(
- """
- INSERT INTO cashu.proofs_used
- (id, amount, C, secret, cashu_id)
- VALUES (?, ?, ?, ?, ?)
- """,
- (invalidate_proof_id, proof.amount, str(proof.C), str(proof.secret), cashu_id),
- )
+# async def invalidate_proof(cashu_id: str, proof: Proof):
+# invalidate_proof_id = urlsafe_short_hash()
+# await db.execute(
+# """
+# INSERT INTO cashu.proofs_used
+# (id, amount, C, secret, cashu_id)
+# VALUES (?, ?, ?, ?, ?)
+# """,
+# (invalidate_proof_id, proof.amount, str(proof.C), str(proof.secret), cashu_id),
+# )
-########################################
-############ MINT INVOICES #############
-########################################
+# ########################################
+# ############ MINT INVOICES #############
+# ########################################
-async def store_lightning_invoice(cashu_id: str, invoice: Invoice):
- await db.execute(
- """
- INSERT INTO cashu.invoices
- (cashu_id, amount, pr, hash, issued)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- cashu_id,
- invoice.amount,
- invoice.pr,
- invoice.hash,
- invoice.issued,
- ),
- )
+# async def store_lightning_invoice(cashu_id: str, invoice: Invoice):
+# await db.execute(
+# """
+# INSERT INTO cashu.invoices
+# (cashu_id, amount, pr, hash, issued)
+# VALUES (?, ?, ?, ?, ?)
+# """,
+# (
+# cashu_id,
+# invoice.amount,
+# invoice.pr,
+# invoice.hash,
+# invoice.issued,
+# ),
+# )
-async def get_lightning_invoice(cashu_id: str, hash: str):
- row = await db.fetchone(
- """
- SELECT * from cashu.invoices
- WHERE cashu_id =? AND hash = ?
- """,
- (
- cashu_id,
- hash,
- ),
- )
- return Invoice.from_row(row)
+# async def get_lightning_invoice(cashu_id: str, hash: str):
+# row = await db.fetchone(
+# """
+# SELECT * from cashu.invoices
+# WHERE cashu_id =? AND hash = ?
+# """,
+# (
+# cashu_id,
+# hash,
+# ),
+# )
+# return Invoice.from_row(row)
-async def update_lightning_invoice(cashu_id: str, hash: str, issued: bool):
- await db.execute(
- "UPDATE cashu.invoices SET issued = ? WHERE cashu_id = ? AND hash = ?",
- (
- issued,
- cashu_id,
- hash,
- ),
- )
+# async def update_lightning_invoice(cashu_id: str, hash: str, issued: bool):
+# await db.execute(
+# "UPDATE cashu.invoices SET issued = ? WHERE cashu_id = ? AND hash = ?",
+# (
+# issued,
+# cashu_id,
+# hash,
+# ),
+# )
##############################
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index 3f799534..ec520765 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -30,118 +30,3 @@ async def m001_initial(db):
);
"""
)
-
-
-# async def m001_initial(db):
-# await db.execute(
-# """
-# CREATE TABLE IF NOT EXISTS cashu.promises (
-# amount INTEGER NOT NULL,
-# B_b TEXT NOT NULL,
-# C_b TEXT NOT NULL,
-
-# UNIQUE (B_b)
-
-# );
-# """
-# )
-
-# await db.execute(
-# """
-# CREATE TABLE IF NOT EXISTS cashu.proofs_used (
-# amount INTEGER NOT NULL,
-# C TEXT NOT NULL,
-# secret TEXT NOT NULL,
-
-# UNIQUE (secret)
-
-# );
-# """
-# )
-
-# await db.execute(
-# """
-# CREATE TABLE IF NOT EXISTS cashu.invoices (
-# amount INTEGER NOT NULL,
-# pr TEXT NOT NULL,
-# hash TEXT NOT NULL,
-# issued BOOL NOT NULL,
-
-# UNIQUE (hash)
-
-# );
-# """
-# )
-
-# await db.execute(
-# """
-# CREATE VIEW IF NOT EXISTS cashu.balance_issued AS
-# SELECT COALESCE(SUM(s), 0) AS balance FROM (
-# SELECT SUM(amount) AS s
-# FROM cashu.promises
-# WHERE amount > 0
-# );
-# """
-# )
-
-# await db.execute(
-# """
-# CREATE VIEW IF NOT EXISTS cashu.balance_used AS
-# SELECT COALESCE(SUM(s), 0) AS balance FROM (
-# SELECT SUM(amount) AS s
-# FROM cashu.proofs_used
-# WHERE amount > 0
-# );
-# """
-# )
-
-# await db.execute(
-# """
-# CREATE VIEW IF NOT EXISTS cashu.balance AS
-# SELECT s_issued - s_used AS balance FROM (
-# SELECT bi.balance AS s_issued, bu.balance AS s_used
-# FROM cashu.balance_issued bi
-# CROSS JOIN balance_used bu
-# );
-# """
-# )
-
-
-# async def m003_mint_keysets(db):
-# """
-# Stores mint keysets from different mints and epochs.
-# """
-# await db.execute(
-# f"""
-# CREATE TABLE IF NOT EXISTS cashu.keysets (
-# id TEXT NOT NULL,
-# derivation_path TEXT,
-# valid_from TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
-# valid_to TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
-# first_seen TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
-# active BOOL DEFAULT TRUE,
-
-# UNIQUE (derivation_path)
-
-# );
-# """
-# )
-# await db.execute(
-# f"""
-# CREATE TABLE IF NOT EXISTS cashu.mint_pubkeys (
-# id TEXT NOT NULL,
-# amount INTEGER NOT NULL,
-# pubkey TEXT NOT NULL,
-
-# UNIQUE (id, pubkey)
-
-# );
-# """
-# )
-
-
-# async def m004_keysets_add_version(db):
-# """
-# Column that remembers with which version
-# """
-# await db.execute("ALTER TABLE cashu.keysets ADD COLUMN version TEXT")
diff --git a/lnbits/extensions/cashu/mint.py b/lnbits/extensions/cashu/mint.py
deleted file mode 100644
index 4153fb30..00000000
--- a/lnbits/extensions/cashu/mint.py
+++ /dev/null
@@ -1,155 +0,0 @@
-import math
-from typing import List, Set
-
-from lnbits import bolt11
-from lnbits.core.services import check_transaction_status, fee_reserve, pay_invoice
-from lnbits.wallets.base import PaymentStatus
-
-from .core.b_dhke import step2_bob
-from .core.base import BlindedMessage, BlindedSignature, Proof
-from .core.secp import PublicKey
-from .core.split import amount_split
-from .crud import get_proofs_used, invalidate_proof
-from .mint_helper import (
- derive_keys,
- derive_pubkeys,
- verify_equation_balanced,
- verify_no_duplicates,
- verify_outputs,
- verify_proof,
- verify_secret_criteria,
- verify_split_amount,
-)
-from .models import Cashu
-
-# todo: extract const
-MAX_ORDER = 64
-
-
-def get_pubkeys(xpriv: str):
- """Returns public keys for possible amounts."""
-
- keys = derive_keys(xpriv)
- pub_keys = derive_pubkeys(keys)
-
- return {a: p.serialize().hex() for a, p in pub_keys.items()}
-
-
-async def generate_promises(
- master_prvkey: str, amounts: List[int], B_s: List[PublicKey]
-):
- """Mints a promise for coins for B_."""
-
- for amount in amounts:
- if amount not in [2**i for i in range(MAX_ORDER)]:
- raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
-
- promises = [
- await generate_promise(master_prvkey, amount, B_)
- for B_, amount in zip(B_s, amounts)
- ]
- return promises
-
-
-async def generate_promise(master_prvkey: str, amount: int, B_: PublicKey):
- """Generates a promise for given amount and returns a pair (amount, C')."""
- secret_key = derive_keys(master_prvkey)[amount] # Get the correct key
- C_ = step2_bob(B_, secret_key)
- return BlindedSignature(amount=amount, C_=C_.serialize().hex())
-
-
-async def melt(cashu: Cashu, proofs: List[Proof], invoice: str):
- """Invalidates proofs and pays a Lightning invoice."""
- # Verify proofs
- proofs_used: Set[str] = set(await get_proofs_used(cashu.id))
- for p in proofs:
- await verify_proof(cashu.prvkey, proofs_used, p)
-
- total_provided = sum([p["amount"] for p in proofs])
- invoice_obj = bolt11.decode(invoice)
- amount = math.ceil(invoice_obj.amount_msat / 1000)
-
- fees_msat = await check_fees(cashu.wallet, invoice_obj)
- 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 invalidate_proofs(cashu.id, proofs)
- return status.paid, status.preimage
- return False, ""
-
-
-async def check_fees(wallet_id: str, decoded_invoice):
- """Returns the fees (in msat) required to pay this pr."""
- amount = math.ceil(decoded_invoice.amount_msat / 1000)
- status: PaymentStatus = await check_transaction_status(
- wallet_id, decoded_invoice.payment_hash
- )
- fees_msat = fee_reserve(amount * 1000) if status.paid != True else 0
- return fees_msat
-
-
-async def split(
- cashu: Cashu, proofs: List[Proof], amount: int, outputs: List[BlindedMessage]
-):
- """Consumes proofs and prepares new promises based on the amount split."""
- total = sum([p.amount for p in proofs])
-
- # verify that amount is kosher
- verify_split_amount(amount)
- # verify overspending attempt
- if amount > total:
- raise Exception(
- f"split amount ({amount}) is higher than the total sum ({total})."
- )
-
- # Verify secret criteria
- if not all([verify_secret_criteria(p) for p in proofs]):
- raise Exception("secrets do not match criteria.")
- # verify that only unique proofs and outputs were used
- if not verify_no_duplicates(proofs, outputs):
- raise Exception("duplicate proofs or promises.")
- # verify that outputs have the correct amount
- if not verify_outputs(total, amount, outputs): # ?
- raise Exception("split of promises is not as expected.")
- # Verify proofs
- # Verify proofs
- proofs_used: Set[str] = set(await get_proofs_used(cashu.id))
- for p in proofs:
- await verify_proof(cashu.prvkey, proofs_used, p)
-
- # Mark proofs as used and prepare new promises
- await invalidate_proofs(cashu.id, proofs)
-
- outs_fst = amount_split(total - amount)
- outs_snd = amount_split(amount)
- B_fst = [
- PublicKey(bytes.fromhex(od.B_), raw=True) for od in outputs[: len(outs_fst)]
- ]
- B_snd = [
- PublicKey(bytes.fromhex(od.B_), raw=True) for od in outputs[len(outs_fst) :]
- ]
- # PublicKey(bytes.fromhex(payload.B_), raw=True)
- prom_fst, prom_snd = await generate_promises(
- cashu.prvkey, outs_fst, B_fst
- ), await generate_promises(cashu.prvkey, outs_snd, B_snd)
- # verify amounts in produced proofs
- verify_equation_balanced(proofs, prom_fst + prom_snd)
- return prom_fst, prom_snd
-
-
-async def invalidate_proofs(cashu_id: str, proofs: List[Proof]):
- """Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
- for p in proofs:
- await invalidate_proof(cashu_id, p)
diff --git a/lnbits/extensions/cashu/mint_helper.py b/lnbits/extensions/cashu/mint_helper.py
deleted file mode 100644
index 8e7e2275..00000000
--- a/lnbits/extensions/cashu/mint_helper.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import base64
-import hashlib
-from typing import List, Set
-
-from .core.b_dhke import verify
-from .core.base import BlindedSignature
-from .core.secp import PrivateKey, PublicKey
-from .core.split import amount_split
-from .models import BlindedMessage, Proof
-
-# todo: extract const
-MAX_ORDER = 64
-
-
-def derive_keys(master_key: str):
- """Deterministic derivation of keys for 2^n values."""
- return {
- 2
- ** i: PrivateKey(
- hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
- .hexdigest()
- .encode("utf-8")[:32],
- raw=True,
- )
- for i in range(MAX_ORDER)
- }
-
-
-def derive_pubkeys(keys: List[PrivateKey]):
- return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
-
-
-# async required?
-async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
- """Verifies that the proof of promise was issued by this ledger."""
- if proof.secret in proofs_used:
- raise Exception(f"tokens already spent. Secret: {proof.secret}")
-
- secret_key = derive_keys(master_prvkey)[
- proof.amount
- ] # Get the correct key to check against
- C = PublicKey(bytes.fromhex(proof.C), raw=True)
- secret = base64.standard_b64decode(proof.secret)
- print("### secret", secret)
- validMintSig = verify(secret_key, C, secret)
- if validMintSig != True:
- raise Exception(f"tokens not valid. Secret: {proof.secret}")
-
-
-def verify_split_amount(amount: int):
- """Split amount like output amount can't be negative or too big."""
- try:
- verify_amount(amount)
- except:
- # For better error message
- raise Exception("invalid split amount: " + str(amount))
-
-
-def verify_secret_criteria(proof: Proof):
- if proof.secret is None or proof.secret == "":
- raise Exception("no secret in proof.")
- return True
-
-
-def verify_no_duplicates(proofs: List[Proof], outputs: List[BlindedMessage]):
- secrets = [p.secret for p in proofs]
- if len(secrets) != len(list(set(secrets))):
- return False
- B_s = [od.B_ for od in outputs]
- if len(B_s) != len(list(set(B_s))):
- return False
- return True
-
-
-def verify_outputs(total: int, amount: int, outputs: List[BlindedMessage]):
- """Verifies the expected split was correctly computed"""
- frst_amt, scnd_amt = total - amount, amount # we have two amounts to split to
- frst_outputs = amount_split(frst_amt)
- scnd_outputs = amount_split(scnd_amt)
- expected = frst_outputs + scnd_outputs
- given = [o.amount for o in outputs]
- return given == expected
-
-
-def verify_amount(amount: int):
- """Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
- valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
- if not valid:
- raise Exception("invalid amount: " + str(amount))
- return amount
-
-
-def verify_equation_balanced(proofs: List[Proof], outs: List[BlindedSignature]):
- """Verify that Σoutputs - Σinputs = 0."""
- sum_inputs = sum(verify_amount(p.amount) for p in proofs)
- sum_outputs = sum(verify_amount(p.amount) for p in outs)
- assert sum_outputs - sum_inputs == 0
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
index 03bba895..dddd1ef1 100644
--- a/lnbits/extensions/cashu/tasks.py
+++ b/lnbits/extensions/cashu/tasks.py
@@ -1,17 +1,11 @@
import asyncio
import json
-from lnbits.core import db as core_db
-from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import urlsafe_short_hash
-from lnbits.tasks import internal_invoice_queue, register_invoice_listener
+from lnbits.tasks import 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
@@ -35,51 +29,6 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
- if payment.extra.get("tag") == "cashu" and payment.extra.get("tipSplitted"):
- # already splitted, ignore
+ if not payment.extra.get("tag") == "cashu":
return
-
- # now we make some special internal transfers (from no one to the receiver)
- cashu = await get_cashu(payment.extra.get("cashuId"))
- tipAmount = payment.extra.get("tipAmount")
-
- if tipAmount is None:
- # no tip amount
- return
-
- tipAmount = tipAmount * 1000
- amount = payment.amount - tipAmount
-
- # mark the original payment with one extra key, "splitted"
- # (this prevents us from doing this process again and it's informative)
- # and reduce it by the amount we're going to send to the producer
- await core_db.execute(
- """
- UPDATE apipayments
- SET extra = ?, amount = ?
- WHERE hash = ?
- AND checking_id NOT LIKE 'internal_%'
- """,
- (
- json.dumps(dict(**payment.extra, tipSplitted=True)),
- amount,
- payment.payment_hash,
- ),
- )
-
- # perform the internal transfer using the same payment_hash
- internal_checking_id = f"internal_{urlsafe_short_hash()}"
- await create_payment(
- wallet_id=cashu.tip_wallet,
- checking_id=internal_checking_id,
- payment_request="",
- payment_hash=payment.payment_hash,
- amount=tipAmount,
- memo=f"Tip for {payment.memo}",
- pending=False,
- extra={"tipSplitted": True},
- )
-
- # manually send this for now
- await internal_invoice_queue.put(internal_checking_id)
return
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 2d28c78b..6315a765 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -2,6 +2,7 @@ import json
from http import HTTPStatus
from typing import Union
import math
+from typing import Dict, List, Union
import httpx
from fastapi import Query
@@ -25,38 +26,22 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.wallets.base import PaymentStatus
from lnbits.helpers import urlsafe_short_hash
from lnbits.core.crud import check_internal
+
+# --------- extension imports
+
from . import cashu_ext
-from .core.base import CashuError, PostSplitResponse, SplitRequest
from .crud import (
create_cashu,
delete_cashu,
get_cashu,
get_cashus,
- get_lightning_invoice,
- store_lightning_invoice,
- store_promise,
- update_lightning_invoice,
)
-# from .ledger import mint, request_mint
-from .mint import generate_promises, get_pubkeys, melt, split
-from .models import (
- Cashu,
- CheckPayload,
- Invoice,
- MeltPayload,
- MintPayloads,
- PayLnurlWData,
- Pegs,
- SplitPayload,
-)
+from .models import Cashu
+from . import ledger
-############### IMPORT CALLE
-from typing import Dict, List, Union
-
-from secp256k1 import PublicKey
-
+# -------- cashu imports
from cashu.core.base import (
Proof,
BlindedSignature,
@@ -69,9 +54,8 @@ from cashu.core.base import (
MintRequest,
PostSplitResponse,
SplitRequest,
+ Invoice,
)
-from cashu.core.errors import CashuError
-from . import db, ledger
LIGHTNING = False
@@ -165,7 +149,7 @@ async def mint_coins(
data: MintRequest,
cashu_id: str = Query(None),
payment_hash: str = Query(None),
-):
+) -> List[BlindedSignature]:
"""
Requests the minting of tokens belonging to a paid payment request.
Call this endpoint after `GET /mint`.
@@ -220,7 +204,9 @@ async def mint_coins(
@cashu_ext.post("/{cashu_id}/melt")
-async def melt_coins(payload: MeltRequest, cashu_id: str = Query(None)):
+async def melt_coins(
+ payload: MeltRequest, cashu_id: str = Query(None)
+) -> GetMeltResponse:
"""Invalidates proofs and pays a Lightning invoice."""
cashu: Union[None, Cashu] = await get_cashu(cashu_id)
if cashu is None:
@@ -229,8 +215,6 @@ async def melt_coins(payload: MeltRequest, cashu_id: str = Query(None)):
)
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
@@ -271,18 +255,7 @@ async def melt_coins(payload: MeltRequest, cashu_id: str = Query(None)):
)
if status.paid == True:
await ledger._invalidate_proofs(proofs)
- return status.paid, status.preimage
- return False, ""
-
-
-@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
+ return GetMeltResponse(paid=status.paid, preimage=status.preimage)
@cashu_ext.post("/check")
@@ -298,29 +271,46 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
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)
+ invoice_obj = bolt11.decode(payload.pr)
+ 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
return CheckFeesResponse(fee=fees_msat / 1000)
-@cashu_ext.post("/split")
+@cashu_ext.post("/{cashu_id}/split")
async def split(
- payload: SplitRequest,
-) -> Union[CashuError, PostSplitResponse]:
+ payload: SplitRequest, cashu_id: str = Query(None)
+) -> PostSplitResponse:
"""
Requetst a set of tokens with amount "total" to be split into two
newly minted sets with amount "split" and "total-split".
"""
+ 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
amount = payload.amount
- outputs = payload.outputs.blinded_messages if payload.outputs else None
+ outputs = payload.outputs.blinded_messages
# backwards compatibility with clients < v0.2.2
assert outputs, Exception("no outputs provided.")
try:
- split_return = await ledger.split(proofs, amount, outputs)
+ split_return = await ledger.split(proofs, amount, outputs, cashu.keyset_id)
except Exception as exc:
- return CashuError(error=str(exc))
+ HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail=str(exc),
+ )
if not split_return:
- return CashuError(error="there was an error with the split")
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="there was an error with the split",
+ )
frst_promises, scnd_promises = split_return
resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises)
return resp
From 161d49a45046ddd90b1921c02fd33ec5f4b3561d Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 17 Oct 2022 11:03:39 +0200
Subject: [PATCH 0187/1058] clean up
---
lnbits/extensions/cashu/__init__.py | 3 +-
lnbits/extensions/cashu/core/b_dhke.py | 88 -------------
lnbits/extensions/cashu/core/base.py | 168 -------------------------
lnbits/extensions/cashu/core/secp.py | 52 --------
lnbits/extensions/cashu/core/split.py | 8 --
lnbits/extensions/cashu/views_api.py | 41 +++---
6 files changed, 22 insertions(+), 338 deletions(-)
delete mode 100644 lnbits/extensions/cashu/core/b_dhke.py
delete mode 100644 lnbits/extensions/cashu/core/base.py
delete mode 100644 lnbits/extensions/cashu/core/secp.py
delete mode 100644 lnbits/extensions/cashu/core/split.py
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index 6e19eb6e..440be092 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -18,7 +18,6 @@ cashu_static_files = [
"name": "cashu_static",
}
]
-sys.path.append("/Users/cc/git/cashu")
from cashu.mint.ledger import Ledger
ledger = Ledger(
@@ -28,7 +27,7 @@ ledger = Ledger(
derivation_path="0/0/0/1",
)
-cashu_ext: APIRouter = APIRouter(prefix="/api/v1/cashu", tags=["cashu"])
+cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
def cashu_renderer():
diff --git a/lnbits/extensions/cashu/core/b_dhke.py b/lnbits/extensions/cashu/core/b_dhke.py
deleted file mode 100644
index ff0bc515..00000000
--- a/lnbits/extensions/cashu/core/b_dhke.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Don't trust me with cryptography.
-
-"""
-Implementation of https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406
-Alice:
-A = a*G
-return A
-Bob:
-Y = hash_to_curve(secret_message)
-r = random blinding factor
-B'= Y + r*G
-return B'
-Alice:
-C' = a*B'
- (= a*Y + a*r*G)
-return C'
-Bob:
-C = C' - r*A
- (= C' - a*r*G)
- (= a*Y)
-return C, secret_message
-Alice:
-Y = hash_to_curve(secret_message)
-C == a*Y
-If true, C must have originated from Alice
-"""
-
-import hashlib
-
-from secp256k1 import PrivateKey, PublicKey
-
-
-def hash_to_curve(message: bytes):
- """Generates a point from the message hash and checks if the point lies on the curve.
- If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
- point = None
- msg_to_hash = message
- while point is None:
- try:
- _hash = hashlib.sha256(msg_to_hash).digest()
- point = PublicKey(b"\x02" + _hash, raw=True)
- except:
- msg_to_hash = _hash
- return point
-
-
-def step1_alice(secret_msg):
- secret_msg = secret_msg
- Y = hash_to_curve(secret_msg)
- r = PrivateKey()
- B_ = Y + r.pubkey
- return B_, r
-
-
-def step2_bob(B_, a):
- C_ = B_.mult(a)
- return C_
-
-
-def step3_alice(C_, r, A):
- C = C_ - A.mult(r)
- return C
-
-
-def verify(a, C, secret_msg):
- Y = hash_to_curve(secret_msg)
- return C == Y.mult(a)
-
-
-### Below is a test of a simple positive and negative case
-
-# # Alice's keys
-# a = PrivateKey()
-# A = a.pubkey
-# secret_msg = "test"
-# B_, r = step1_alice(secret_msg)
-# C_ = step2_bob(B_, a)
-# C = step3_alice(C_, r, A)
-# print("C:{}, secret_msg:{}".format(C, secret_msg))
-# assert verify(a, C, secret_msg)
-# assert verify(a, C + C, secret_msg) == False # adding C twice shouldn't pass
-# assert verify(a, A, secret_msg) == False # A shouldn't pass
-
-# # Test operations
-# b = PrivateKey()
-# B = b.pubkey
-# assert -A -A + A == -A # neg
-# assert B.mult(a) == A.mult(b) # a*B = A*b
diff --git a/lnbits/extensions/cashu/core/base.py b/lnbits/extensions/cashu/core/base.py
deleted file mode 100644
index 947da987..00000000
--- a/lnbits/extensions/cashu/core/base.py
+++ /dev/null
@@ -1,168 +0,0 @@
-from sqlite3 import Row
-from typing import List, Union
-
-from pydantic import BaseModel
-
-
-class CashuError(BaseException):
- code = "000"
- error = "CashuError"
-
-
-class P2SHScript(BaseModel):
- script: str
- signature: str
- address: Union[str, None] = None
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(
- address=row[0],
- script=row[1],
- signature=row[2],
- used=row[3],
- )
-
-
-class Proof(BaseModel):
- amount: int
- secret: str = ""
- C: str
- script: Union[P2SHScript, None] = None
- reserved: bool = False # whether this proof is reserved for sending
- send_id: str = "" # unique ID of send attempt
- time_created: str = ""
- time_reserved: str = ""
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(
- amount=row[0],
- C=row[1],
- secret=row[2],
- reserved=row[3] or False,
- send_id=row[4] or "",
- time_created=row[5] or "",
- time_reserved=row[6] or "",
- )
-
- @classmethod
- def from_dict(cls, d: dict):
- assert "amount" in d, "no amount in proof"
- return cls(
- amount=d.get("amount"),
- C=d.get("C"),
- secret=d.get("secret") or "",
- reserved=d.get("reserved") or False,
- send_id=d.get("send_id") or "",
- time_created=d.get("time_created") or "",
- time_reserved=d.get("time_reserved") or "",
- )
-
- def to_dict(self):
- return dict(amount=self.amount, secret=self.secret, C=self.C)
-
- def to_dict_no_secret(self):
- return dict(amount=self.amount, C=self.C)
-
- def __getitem__(self, key):
- return self.__getattribute__(key)
-
- def __setitem__(self, key, val):
- self.__setattr__(key, val)
-
-
-class Proofs(BaseModel):
- """TODO: Use this model"""
-
- proofs: List[Proof]
-
-
-class Invoice(BaseModel):
- amount: int
- pr: str
- hash: str
- issued: bool = False
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(
- cashu_id=str(row[0]),
- amount=int(row[1]),
- pr=str(row[2]),
- hash=str(row[3]),
- issued=bool(row[4]),
- )
-
-
-class BlindedMessage(BaseModel):
- amount: int
- B_: str
-
-
-class BlindedSignature(BaseModel):
- amount: int
- C_: str
-
- @classmethod
- def from_dict(cls, d: dict):
- return cls(
- amount=d["amount"],
- C_=d["C_"],
- )
-
-
-class MintRequest(BaseModel):
- blinded_messages: List[BlindedMessage] = []
-
-
-class GetMintResponse(BaseModel):
- pr: str
- hash: str
-
-
-class GetMeltResponse(BaseModel):
- paid: Union[bool, None]
- preimage: Union[str, None]
-
-
-class SplitRequest(BaseModel):
- proofs: List[Proof]
- amount: int
- output_data: Union[
- MintRequest, None
- ] = None # backwards compatibility with clients < v0.2.2
- outputs: Union[MintRequest, None] = None
-
- def __init__(self, **data):
- super().__init__(**data)
- self.backwards_compatibility_v021()
-
- def backwards_compatibility_v021(self):
- # before v0.2.2: output_data, after: outputs
- if self.output_data:
- self.outputs = self.output_data
- self.output_data = None
-
-
-class PostSplitResponse(BaseModel):
- fst: List[BlindedSignature]
- snd: List[BlindedSignature]
-
-
-class CheckRequest(BaseModel):
- proofs: List[Proof]
-
-
-class CheckFeesRequest(BaseModel):
- pr: str
-
-
-class CheckFeesResponse(BaseModel):
- fee: Union[int, None]
-
-
-class MeltRequest(BaseModel):
- proofs: List[Proof]
- amount: int = None # deprecated
- invoice: str
diff --git a/lnbits/extensions/cashu/core/secp.py b/lnbits/extensions/cashu/core/secp.py
deleted file mode 100644
index 33416434..00000000
--- a/lnbits/extensions/cashu/core/secp.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from secp256k1 import PrivateKey, PublicKey
-
-
-# We extend the public key to define some operations on points
-# Picked from https://github.com/WTRMQDev/secp256k1-zkp-py/blob/master/secp256k1_zkp/__init__.py
-class PublicKeyExt(PublicKey):
- def __add__(self, pubkey2):
- if isinstance(pubkey2, PublicKey):
- new_pub = PublicKey()
- new_pub.combine([self.public_key, pubkey2.public_key])
- return new_pub
- else:
- raise TypeError("Cant add pubkey and %s" % pubkey2.__class__)
-
- def __neg__(self):
- serialized = self.serialize()
- first_byte, remainder = serialized[:1], serialized[1:]
- # flip odd/even byte
- first_byte = {b"\x03": b"\x02", b"\x02": b"\x03"}[first_byte]
- return PublicKey(first_byte + remainder, raw=True)
-
- def __sub__(self, pubkey2):
- if isinstance(pubkey2, PublicKey):
- return self + (-pubkey2)
- else:
- raise TypeError("Can't add pubkey and %s" % pubkey2.__class__)
-
- def mult(self, privkey):
- if isinstance(privkey, PrivateKey):
- return self.tweak_mul(privkey.private_key)
- else:
- raise TypeError("Can't multiply with non privatekey")
-
- def __eq__(self, pubkey2):
- if isinstance(pubkey2, PublicKey):
- seq1 = self.to_data()
- seq2 = pubkey2.to_data()
- return seq1 == seq2
- else:
- raise TypeError("Can't compare pubkey and %s" % pubkey2.__class__)
-
- def to_data(self):
- return [self.public_key.data[i] for i in range(64)]
-
-
-# Horrible monkeypatching
-PublicKey.__add__ = PublicKeyExt.__add__
-PublicKey.__neg__ = PublicKeyExt.__neg__
-PublicKey.__sub__ = PublicKeyExt.__sub__
-PublicKey.mult = PublicKeyExt.mult
-PublicKey.__eq__ = PublicKeyExt.__eq__
-PublicKey.to_data = PublicKeyExt.to_data
diff --git a/lnbits/extensions/cashu/core/split.py b/lnbits/extensions/cashu/core/split.py
deleted file mode 100644
index 44b9cf51..00000000
--- a/lnbits/extensions/cashu/core/split.py
+++ /dev/null
@@ -1,8 +0,0 @@
-def amount_split(amount):
- """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8]."""
- bits_amt = bin(amount)[::-1][:-2]
- rv = []
- for (pos, bit) in enumerate(bits_amt):
- if bit == "1":
- rv.append(2**pos)
- return rv
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 6315a765..b271bccc 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -64,7 +64,7 @@ LIGHTNING = False
########################################
# todo: use /mints
-@cashu_ext.get("/cashus", status_code=HTTPStatus.OK)
+@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK)
async def api_cashus(
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -75,7 +75,7 @@ async def api_cashus(
return [cashu.dict() for cashu in await get_cashus(wallet_ids)]
-@cashu_ext.post("/cashus", status_code=HTTPStatus.CREATED)
+@cashu_ext.post("/api/v1/cashus", status_code=HTTPStatus.CREATED)
async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)):
cashu_id = urlsafe_short_hash()
# generate a new keyset in cashu
@@ -93,7 +93,7 @@ async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key
#######################################
-@cashu_ext.get("/{cashu_id}/keys", status_code=HTTPStatus.OK)
+@cashu_ext.get("/api/v1/cashu/{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: Union[Cashu, None] = await get_cashu(cashu_id)
@@ -106,7 +106,7 @@ async def keys(cashu_id: str = Query(None)) -> dict[int, str]:
return ledger.get_keyset(keyset_id=cashu.keyset_id)
-@cashu_ext.get("/{cashu_id}/mint")
+@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
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.
@@ -144,7 +144,7 @@ async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintR
return resp
-@cashu_ext.post("/{cashu_id}/mint")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
async def mint_coins(
data: MintRequest,
cashu_id: str = Query(None),
@@ -203,7 +203,7 @@ async def mint_coins(
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-@cashu_ext.post("/{cashu_id}/melt")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/melt")
async def melt_coins(
payload: MeltRequest, cashu_id: str = Query(None)
) -> GetMeltResponse:
@@ -258,13 +258,13 @@ async def melt_coins(
return GetMeltResponse(paid=status.paid, preimage=status.preimage)
-@cashu_ext.post("/check")
+@cashu_ext.post("/api/v1/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")
+@cashu_ext.post("/api/v1/checkfees")
async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
"""
Responds with the fees necessary to pay a Lightning invoice.
@@ -281,7 +281,7 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
return CheckFeesResponse(fee=fees_msat / 1000)
-@cashu_ext.post("/{cashu_id}/split")
+@cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
async def split(
payload: SplitRequest, cashu_id: str = Query(None)
) -> PostSplitResponse:
@@ -299,6 +299,7 @@ async def split(
outputs = payload.outputs.blinded_messages
# backwards compatibility with clients < v0.2.2
assert outputs, Exception("no outputs provided.")
+ split_return = None
try:
split_return = await ledger.split(proofs, amount, outputs, cashu.keyset_id)
except Exception as exc:
@@ -316,7 +317,7 @@ async def split(
return resp
-# @cashu_ext.post("/api/v1/cashus/upodatekeys", status_code=HTTPStatus.CREATED)
+# @cashu_ext.post("/api/v1s/upodatekeys", status_code=HTTPStatus.CREATED)
# async def api_cashu_update_keys(
# data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)
# ):
@@ -327,7 +328,7 @@ async def split(
# return cashu.dict()
-# @cashu_ext.delete("/api/v1/cashus/{cashu_id}")
+# @cashu_ext.delete("/api/v1s/{cashu_id}")
# async def api_cashu_delete(
# cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
# ):
@@ -348,7 +349,7 @@ async def split(
# ########################################
# #################????###################
# ########################################
-# @cashu_ext.post("/api/v1/cashus/{cashu_id}/invoices", status_code=HTTPStatus.CREATED)
+# @cashu_ext.post("/api/v1s/{cashu_id}/invoices", status_code=HTTPStatus.CREATED)
# async def api_cashu_create_invoice(
# amount: int = Query(..., ge=1), tipAmount: int = None, cashu_id: str = None
# ):
@@ -376,7 +377,7 @@ async def split(
# @cashu_ext.post(
-# "/api/v1/cashus/{cashu_id}/invoices/{payment_request}/pay",
+# "/api/v1s/{cashu_id}/invoices/{payment_request}/pay",
# status_code=HTTPStatus.OK,
# )
# async def api_cashu_pay_invoice(
@@ -437,7 +438,7 @@ async def split(
# @cashu_ext.get(
-# "/api/v1/cashus/{cashu_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
+# "/api/v1s/{cashu_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
# )
# async def api_cashu_check_invoice(cashu_id: str, payment_hash: str):
# cashu = await get_cashu(cashu_id)
@@ -459,7 +460,7 @@ async def split(
# ########################################
-# # @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(False)):
# # """Get the public keys of the mint"""
# # mint = await get_cashu(cashu_id)
@@ -470,7 +471,7 @@ async def split(
# # return get_pubkeys(mint.prvkey)
-# @cashu_ext.get("/api/v1/cashu/{cashu_id}/mint")
+# @cashu_ext.get("/api/v1/{cashu_id}/mint")
# async def mint_pay_request(amount: int = 0, cashu_id: str = Query(None)):
# """Request minting of tokens. Server responds with a Lightning invoice."""
@@ -498,7 +499,7 @@ async def split(
# return {"pr": payment_request, "hash": payment_hash}
-# @cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
+# @cashu_ext.post("/api/v1/{cashu_id}/mint")
# async def mint_coins(
# data: MintPayloads,
# cashu_id: str = Query(None),
@@ -560,7 +561,7 @@ async def split(
# 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(payload: MeltPayload, cashu_id: str = Query(None)):
# """Invalidates proofs and pays a Lightning invoice."""
# cashu: Cashu = await get_cashu(cashu_id)
@@ -576,12 +577,12 @@ async def split(
# raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
-# @cashu_ext.post("/api/v1/cashu/{cashu_id}/check")
+# @cashu_ext.post("/api/v1/{cashu_id}/check")
# async def check_spendable_coins(payload: CheckPayload, cashu_id: str = Query(None)):
# return await check_spendable(payload.proofs, cashu_id)
-# @cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
+# @cashu_ext.post("/api/v1/{cashu_id}/split")
# async def split_proofs(payload: SplitRequest, cashu_id: str = Query(None)):
# """
# Requetst a set of tokens with amount "total" to be split into two
From 0f3ac18f34b113a6986def154d697f6d3cc48b52 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Tue, 18 Oct 2022 11:20:40 +0200
Subject: [PATCH 0188/1058] remove unused import
---
lnbits/extensions/cashu/crud.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 2057f6ff..7ecf9b6a 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -12,7 +12,6 @@ from lnbits.helpers import urlsafe_short_hash
from . import db
-from .core.base import Invoice
from .models import Cashu, Pegs, Promises, Proof
from cashu.core.base import MintKeyset
From ef056d1647ba0f293502b0b70259ba244ac9076f Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Tue, 18 Oct 2022 11:20:53 +0200
Subject: [PATCH 0189/1058] restore pyproject
---
poetry.lock | 71 +++++++++++++++++++-------------------------------
pyproject.toml | 2 +-
2 files changed, 28 insertions(+), 45 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 3869f19b..a5535737 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -100,11 +100,11 @@ python-versions = "*"
[[package]]
name = "black"
-version = "22.8.0"
+version = "22.10.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
-python-versions = ">=3.6.2"
+python-versions = ">=3.7"
[package.dependencies]
click = ">=8.0.0"
@@ -839,7 +839,7 @@ cffi = ">=1.3.0"
[[package]]
name = "setuptools"
-version = "65.4.1"
+version = "65.5.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
@@ -935,17 +935,6 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""
[package.extras]
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
-[[package]]
-name = "starlette-context"
-version = "0.3.4"
-description = "Access context in Starlette"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-starlette = "*"
-
[[package]]
name = "tomli"
version = "2.0.1"
@@ -1062,7 +1051,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "307f1d22c4cd0aecaac7b1bef917c260373f784f88538ff3f7f8cf45eb485372"
+content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
[metadata.files]
aiofiles = [
@@ -1103,29 +1092,27 @@ bitstring = [
{file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
]
black = [
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
- {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
- {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
- {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
- {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
- {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
- {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
- {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
- {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
- {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
- {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
- {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
- {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
- {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
- {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
- {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
- {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
- {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
- {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
- {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
- {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
+ {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
+ {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
+ {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
+ {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
+ {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
+ {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
+ {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
+ {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
+ {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
+ {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
+ {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
+ {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
+ {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
+ {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
+ {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
+ {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
+ {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
+ {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
+ {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
+ {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
+ {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
@@ -1810,8 +1797,8 @@ secp256k1 = [
{file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
]
setuptools = [
- {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
- {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
+ {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"},
+ {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"},
]
shortuuid = [
{file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
@@ -1876,10 +1863,6 @@ starlette = [
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
]
-starlette-context = [
- {file = "starlette_context-0.3.4-py37-none-any.whl", hash = "sha256:b16bf17bd3ead7ded2f458aebf7f913744b9cf28305e16c69b435a6c6ddf1135"},
- {file = "starlette_context-0.3.4.tar.gz", hash = "sha256:2d28e1838302fb5d5adacadc10fb73fb2d5cca1f0aa1e279698701cc96f1567c"},
-]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
diff --git a/pyproject.toml b/pyproject.toml
index bd1cdfd1..0a96c03c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,7 +64,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
-starlette-context = "^0.3.4"
+
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From c10a2b647fb8ac1d1124094626c92994039d0444 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Tue, 18 Oct 2022 11:24:45 +0200
Subject: [PATCH 0190/1058] can install cashu 0.4.1
---
poetry.lock | 295 ++++++++++++++++++++++++++++++++-----------------
pyproject.toml | 19 ++--
2 files changed, 203 insertions(+), 111 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index a5535737..8d6ffefa 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -121,6 +121,36 @@ d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
+[[package]]
+name = "cashu"
+version = "0.4.1"
+description = "Ecash wallet and mint."
+category = "main"
+optional = false
+python-versions = "^3.7"
+develop = false
+
+[package.dependencies]
+bech32 = "^1.2.0"
+bitstring = "^3.1.9"
+click = "8.0.4"
+ecdsa = "^0.18.0"
+environs = "^9.5.0"
+fastapi = "^0.83.0"
+loguru = "^0.6.0"
+pydantic = "^1.10.2"
+pytest-asyncio = "0.19.0"
+python-bitcoinlib = "^0.11.2"
+requests = "2.27.1"
+secp256k1 = "^0.14.0"
+SQLAlchemy = "1.3.24"
+sqlalchemy-aio = "^0.17.0"
+uvicorn = "^0.18.3"
+
+[package.source]
+type = "directory"
+url = "../cashu"
+
[[package]]
name = "Cerberus"
version = "1.3.4"
@@ -164,7 +194,7 @@ unicode_backport = ["unicodedata2"]
[[package]]
name = "click"
-version = "8.0.1"
+version = "8.0.4"
description = "Composable command line interface toolkit"
category = "main"
optional = false
@@ -229,7 +259,7 @@ test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0
[[package]]
name = "ecdsa"
-version = "0.17.0"
+version = "0.18.0"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
@@ -260,7 +290,7 @@ python-versions = "*"
[[package]]
name = "environs"
-version = "9.3.3"
+version = "9.5.0"
description = "simplified environment variable parsing"
category = "main"
optional = false
@@ -271,14 +301,14 @@ marshmallow = ">=3.0.0"
python-dotenv = "*"
[package.extras]
-dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
+dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
[[package]]
name = "fastapi"
-version = "0.78.0"
+version = "0.83.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
@@ -290,9 +320,9 @@ starlette = "0.19.1"
[package.extras]
all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
-dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
-test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
+test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
[[package]]
name = "grpcio"
@@ -394,7 +424,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517",
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
-category = "dev"
+category = "main"
optional = false
python-versions = "*"
@@ -441,7 +471,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "loguru"
-version = "0.5.3"
+version = "0.6.0"
description = "Python logging made (stupidly) simple"
category = "main"
optional = false
@@ -452,7 +482,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
-dev = ["Sphinx (>=2.2.1)", "black (>=19.10b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"]
+dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
[[package]]
name = "MarkupSafe"
@@ -576,7 +606,7 @@ test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.6"
@@ -607,7 +637,7 @@ python-versions = ">=3.6"
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -629,14 +659,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pydantic"
-version = "1.8.2"
-description = "Data validation and settings management using python 3.6 type hinting"
+version = "1.10.2"
+description = "Data validation and settings management using python type hints"
category = "main"
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.7"
[package.dependencies]
-typing-extensions = ">=3.7.4.3"
+typing-extensions = ">=4.1.0"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
@@ -732,7 +762,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
name = "pytest"
version = "7.1.3"
description = "pytest: simple powerful testing with Python"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -753,7 +783,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
name = "pytest-asyncio"
version = "0.19.0"
description = "Pytest support for asyncio"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -779,6 +809,14 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+[[package]]
+name = "python-bitcoinlib"
+version = "0.11.2"
+description = "The Swiss Army Knife of the Bitcoin protocol."
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "python-dotenv"
version = "0.19.0"
@@ -812,6 +850,24 @@ six = ">=1.8.0"
[package.extras]
test = ["ipython", "mock", "pytest (>=3.0.5)"]
+[[package]]
+name = "requests"
+version = "2.27.1"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+
[[package]]
name = "rfc3986"
version = "1.5.0"
@@ -876,7 +932,7 @@ python-versions = ">=3.5"
[[package]]
name = "SQLAlchemy"
-version = "1.3.23"
+version = "1.3.24"
description = "Database Abstraction Library"
category = "main"
optional = false
@@ -939,7 +995,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -961,15 +1017,28 @@ python-versions = "*"
[[package]]
name = "typing-extensions"
-version = "3.10.0.2"
-description = "Backported and Experimental Type Hints for Python 3.5+"
+version = "4.4.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
+
+[[package]]
+name = "urllib3"
+version = "1.26.12"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "uvicorn"
-version = "0.18.1"
+version = "0.18.3"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@@ -981,7 +1050,7 @@ h11 = ">=0.8"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
+standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
[[package]]
name = "uvloop"
@@ -1051,7 +1120,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
+content-hash = "8825e973ce84c5cf75cf5180e8b6519043265e0633154b3237bb741866a8c4b5"
[metadata.files]
aiofiles = [
@@ -1114,6 +1183,7 @@ black = [
{file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
+cashu = []
Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
@@ -1178,8 +1248,8 @@ charset-normalizer = [
{file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
]
click = [
- {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
- {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+ {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
+ {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
]
coincurve = [
{file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"},
@@ -1296,8 +1366,8 @@ cryptography = [
{file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
]
ecdsa = [
- {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
- {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
+ {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
+ {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
]
embit = [
{file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"},
@@ -1308,12 +1378,12 @@ enum34 = [
{file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"},
]
environs = [
- {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
- {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
+ {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
+ {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
]
fastapi = [
- {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
- {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
+ {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"},
+ {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
]
grpcio = [
{file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
@@ -1435,8 +1505,8 @@ lnurl = [
{file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"},
]
loguru = [
- {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
- {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
+ {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
+ {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
]
MarkupSafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
@@ -1662,28 +1732,42 @@ pycryptodomex = [
{file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
]
pydantic = [
- {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
- {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
- {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
- {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
- {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
- {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
- {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
- {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
- {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
- {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+ {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
+ {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
+ {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
+ {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
+ {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
+ {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
+ {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
+ {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
+ {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
+ {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
+ {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
+ {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
+ {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
+ {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
+ {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
+ {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
+ {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
+ {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
+ {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
+ {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
+ {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
+ {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
+ {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
+ {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
+ {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
+ {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
+ {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
+ {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
+ {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
+ {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
+ {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
+ {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
+ {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
+ {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
+ {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
+ {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
]
pyln-bolt7 = [
{file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"},
@@ -1728,6 +1812,10 @@ pytest-cov = [
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
+python-bitcoinlib = [
+ {file = "python-bitcoinlib-0.11.2.tar.gz", hash = "sha256:61ba514e0d232cc84741e49862dcedaf37199b40bba252a17edc654f63d13f39"},
+ {file = "python_bitcoinlib-0.11.2-py3-none-any.whl", hash = "sha256:78bd4ee717fe805cd760dfdd08765e77b7c7dbef4627f8596285e84953756508"},
+]
python-dotenv = [
{file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
{file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
@@ -1767,6 +1855,10 @@ Represent = [
{file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"},
{file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"},
]
+requests = [
+ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+]
rfc3986 = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
@@ -1813,44 +1905,40 @@ sniffio = [
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
]
SQLAlchemy = [
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"},
- {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"},
- {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"},
- {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"},
- {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"},
- {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"},
- {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"},
- {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"},
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"},
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"},
+ {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-win32.whl", hash = "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7"},
+ {file = "SQLAlchemy-1.3.24-cp35-cp35m-win_amd64.whl", hash = "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-win32.whl", hash = "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996"},
+ {file = "SQLAlchemy-1.3.24-cp36-cp36m-win_amd64.whl", hash = "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-win32.whl", hash = "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c"},
+ {file = "SQLAlchemy-1.3.24-cp37-cp37m-win_amd64.whl", hash = "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-win32.whl", hash = "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba"},
+ {file = "SQLAlchemy-1.3.24-cp38-cp38-win_amd64.whl", hash = "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-win32.whl", hash = "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064"},
+ {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"},
+ {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"},
]
sqlalchemy-aio = [
{file = "sqlalchemy_aio-0.17.0-py3-none-any.whl", hash = "sha256:3f4aa392c38f032d6734826a4138a0f02ed3122d442ed142be1e5964f2a33b60"},
@@ -1898,13 +1986,16 @@ types-protobuf = [
{file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"},
]
typing-extensions = [
- {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
- {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
- {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
+ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
+ {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
uvicorn = [
- {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"},
- {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"},
+ {file = "uvicorn-0.18.3-py3-none-any.whl", hash = "sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af"},
+ {file = "uvicorn-0.18.3.tar.gz", hash = "sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
diff --git a/pyproject.toml b/pyproject.toml
index 0a96c03c..37fd7aed 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,11 +17,11 @@ bech32 = "1.2.0"
bitstring = "3.1.9"
certifi = "2021.5.30"
charset-normalizer = "2.0.6"
-click = "8.0.1"
-ecdsa = "0.17.0"
+click = "8.0.4"
+ecdsa = "0.18.0"
embit = "0.4.9"
-environs = "9.3.3"
-fastapi = "0.78.0"
+environs = "9.5.0"
+fastapi = "0.83.0"
h11 = "0.12.0"
httpcore = "0.15.0"
httptools = "0.4.0"
@@ -35,7 +35,7 @@ marshmallow = "3.17.0"
outcome = "1.1.0"
psycopg2-binary = "2.9.1"
pycryptodomex = "3.14.1"
-pydantic = "1.8.2"
+pydantic = "1.10.2"
pypng = "0.0.21"
pyqrcode = "1.2.1"
pyScss = "1.4.0"
@@ -47,16 +47,16 @@ secp256k1 = "0.14.0"
shortuuid = "1.0.1"
six = "1.16.0"
sniffio = "1.2.0"
-sqlalchemy = "1.3.23"
+sqlalchemy = "1.3.24"
sqlalchemy-aio = "0.17.0"
sse-starlette = "0.6.2"
-typing-extensions = "3.10.0.2"
-uvicorn = "0.18.1"
+typing-extensions = "^4.4.0"
+uvicorn = "0.18.3"
uvloop = "0.16.0"
watchgod = "0.7"
websockets = "10.0"
zipp = "3.5.0"
-loguru = "0.5.3"
+loguru = "0.6.0"
cffi = "1.15.0"
websocket-client = "1.3.3"
grpcio = "^1.49.1"
@@ -64,6 +64,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
+cashu = {path = "../cashu"}
[tool.poetry.dev-dependencies]
From 89f55e50b1a718f763c7d89ab13ddb1b37baddf5 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Tue, 18 Oct 2022 11:49:20 +0200
Subject: [PATCH 0191/1058] disable codecov check
---
.github/codecov.yml | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 .github/codecov.yml
diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 00000000..e190e6aa
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,9 @@
+coverage:
+ status:
+ patch: off
+ project:
+ default:
+ target: auto
+ # adjust accordingly based on how flaky your tests are
+ # this allows a 10% drop from the previous base commit coverage
+ threshold: 10%
\ No newline at end of file
From 47c334de7a548fd82418e8a266e11d836345286f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 10:00:47 +0200
Subject: [PATCH 0192/1058] add loop to uvicorn
---
lnbits/server.py | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/lnbits/server.py b/lnbits/server.py
index 6d4cd2e7..eb7c12b1 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -1,12 +1,7 @@
-import asyncio
-
import uvloop
-
uvloop.install()
-import contextlib
import multiprocessing as mp
-import sys
import time
import click
@@ -49,6 +44,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
while True:
config = uvicorn.Config(
"lnbits.__main__:app",
+ loop="uvloop",
port=port,
host=host,
reload=reload,
@@ -65,9 +61,10 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
server_restart.clear()
server.should_exit = True
server.force_exit = True
+ time.sleep(3)
process.terminate()
process.join()
- time.sleep(3)
+ time.sleep(1)
server_restart = mp.Event()
From 1b675f295bd2ec1a69ae972cce9b70f0371eefdc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 11:13:40 +0200
Subject: [PATCH 0193/1058] add get settings endpoint with only values you can
also save
---
lnbits/app.py | 6 +----
lnbits/extensions/admin/crud.py | 12 +++++++++-
lnbits/extensions/admin/models.py | 6 ++++-
.../admin/templates/admin/index.html | 22 +++++++++++++++----
lnbits/extensions/admin/views_api.py | 7 +++++-
lnbits/server.py | 1 +
6 files changed, 42 insertions(+), 12 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 49ad8d77..959a8168 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -62,10 +62,6 @@ def create_app() -> FastAPI:
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)
- # TODO: why those 2?
- g().config = settings
- g().base_url = f"http://{settings.host}:{settings.port}"
-
app.add_middleware(GZipMiddleware, minimum_size=1000)
register_startup(app)
@@ -174,7 +170,7 @@ def register_assets(app: FastAPI):
@app.on_event("startup")
async def vendored_assets_variable():
- if g().config.debug:
+ if settings.debug:
g().VENDORED_JS = map(url_for_vendored, get_js_vendored())
g().VENDORED_CSS = map(url_for_vendored, get_css_vendored())
else:
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index cc937b5e..2ce91612 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -6,7 +6,7 @@ from lnbits.settings import Settings, read_only_variables
from lnbits.tasks import internal_invoice_queue
from . import db
-from .models import UpdateSettings
+from .models import AdminSettings, UpdateSettings
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -26,6 +26,16 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
await internal_invoice_queue.put(internal_id)
+async def get_settings() -> AdminSettings:
+ row = await db.fetchone("SELECT * FROM admin.settings")
+ all_settings = Settings(**row)
+ settings = AdminSettings()
+ for key, value in row.items():
+ if hasattr(settings, key):
+ setattr(settings, key, getattr(all_settings, key))
+ return settings
+
+
async def update_settings(data: UpdateSettings) -> Settings:
fields = []
for key, value in data.dict(exclude_none=True).items():
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index d9d2b22f..31811659 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,4 +1,4 @@
-from typing import List
+from typing import List, Optional
from fastapi import Query
from pydantic import BaseModel
@@ -55,3 +55,7 @@ class UpdateSettings(BaseModel):
opennode_key: str = Query(None)
spark_url: str = Query(None)
spark_token: str = Query(None)
+
+
+class AdminSettings(UpdateSettings):
+ lnbits_allowed_funding_sources: Optional[List[str]]
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 7d268301..10391261 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -314,11 +314,8 @@
}
},
created: function () {
- this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data
+ this.getSettings()
this.balance = +'{{ balance|safe }}'
- this.formData = _.clone(this.settings) //model
- this.updateFundingData()
- console.log(this.settings)
},
computed: {
checkChanges() {
@@ -416,6 +413,23 @@
})
})
},
+ getSettings() {
+ LNbits.api
+ .request(
+ 'GET',
+ '/admin/api/v1/settings/?usr=' + this.g.user.id,
+ this.g.user.wallets[0].adminkey
+ )
+ .then(response => {
+ this.settings = response.data
+ this.formData = _.clone(this.settings)
+ this.updateFundingData()
+ console.log(this.settings)
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
updateSettings() {
let data = {
...this.formData
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 63ed5b3c..57d62ed4 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -10,7 +10,7 @@ from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import UpdateSettings
from lnbits.server import server_restart
-from .crud import delete_settings, update_settings, update_wallet_balance
+from .crud import delete_settings, get_settings, update_settings, update_wallet_balance
@admin_ext.get(
@@ -21,6 +21,11 @@ async def api_restart_server() -> dict[str, str]:
return {"status": "Success"}
+@admin_ext.get("/api/v1/settings/", dependencies=[Depends(check_admin)])
+async def api_get_settings() -> UpdateSettings:
+ return await get_settings()
+
+
@admin_ext.put(
"/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
diff --git a/lnbits/server.py b/lnbits/server.py
index eb7c12b1..ecf7ff62 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -1,4 +1,5 @@
import uvloop
+
uvloop.install()
import multiprocessing as mp
From ec6bb1624a0a522dfc924ac6746af21706f17cd0 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 21 Oct 2022 15:15:43 +0200
Subject: [PATCH 0194/1058] adjust versions to import cashu
---
poetry.lock | 424 +++++++++++++++++++++++++++----------------------
pyproject.toml | 24 +--
2 files changed, 244 insertions(+), 204 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 8d6ffefa..9c426798 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,7 +8,7 @@ python-versions = ">=3.6,<4.0"
[[package]]
name = "anyio"
-version = "3.6.1"
+version = "3.6.2"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false
@@ -22,7 +22,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
-trio = ["trio (>=0.16)"]
+trio = ["trio (>=0.16,<0.22)"]
[[package]]
name = "asgiref"
@@ -59,17 +59,17 @@ typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
[[package]]
name = "attrs"
-version = "21.2.0"
+version = "22.1.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.5"
[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"]
+dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
+tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
[[package]]
name = "base58"
@@ -123,33 +123,56 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cashu"
-version = "0.4.1"
-description = "Ecash wallet and mint."
+version = "0.4.2"
+description = "Ecash wallet and mint with Bitcoin Lightning support"
category = "main"
optional = false
-python-versions = "^3.7"
-develop = false
+python-versions = ">=3.7"
[package.dependencies]
-bech32 = "^1.2.0"
-bitstring = "^3.1.9"
-click = "8.0.4"
-ecdsa = "^0.18.0"
-environs = "^9.5.0"
-fastapi = "^0.83.0"
-loguru = "^0.6.0"
-pydantic = "^1.10.2"
-pytest-asyncio = "0.19.0"
-python-bitcoinlib = "^0.11.2"
-requests = "2.27.1"
-secp256k1 = "^0.14.0"
-SQLAlchemy = "1.3.24"
-sqlalchemy-aio = "^0.17.0"
-uvicorn = "^0.18.3"
-
-[package.source]
-type = "directory"
-url = "../cashu"
+anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+py = {version = "1.11.0", 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\""}
+pyparsing = {version = "3.0.9", 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\""}
+python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
+uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
[[package]]
name = "Cerberus"
@@ -164,15 +187,15 @@ setuptools = "*"
[[package]]
name = "certifi"
-version = "2021.5.30"
+version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.6"
[[package]]
name = "cffi"
-version = "1.15.0"
+version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
@@ -183,7 +206,7 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
-version = "2.0.6"
+version = "2.0.12"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
@@ -326,7 +349,7 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (
[[package]]
name = "grpcio"
-version = "1.49.1"
+version = "1.50.0"
description = "HTTP/2-based RPC framework"
category = "main"
optional = false
@@ -336,7 +359,7 @@ python-versions = ">=3.7"
six = ">=1.5.2"
[package.extras]
-protobuf = ["grpcio-tools (>=1.49.1)"]
+protobuf = ["grpcio-tools (>=1.50.0)"]
[[package]]
name = "h11"
@@ -397,7 +420,7 @@ socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "idna"
-version = "3.2"
+version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
@@ -405,20 +428,20 @@ python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
-version = "4.8.1"
+version = "5.0.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-perf (>=0.9.2)"]
+testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "iniconfig"
@@ -494,7 +517,7 @@ python-versions = ">=3.6"
[[package]]
name = "marshmallow"
-version = "3.17.0"
+version = "3.18.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "main"
optional = false
@@ -504,9 +527,9 @@ python-versions = ">=3.7"
packaging = ">=17.0"
[package.extras]
-dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
-docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.8)", "sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
-lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)"]
+dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
+docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
+lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -551,11 +574,11 @@ python-versions = "*"
[[package]]
name = "outcome"
-version = "1.1.0"
+version = "1.2.0"
description = "Capture the outcome of Python function calls."
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
attrs = ">=19.2.0"
@@ -619,7 +642,7 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "protobuf"
-version = "4.21.7"
+version = "4.21.8"
description = ""
category = "main"
optional = false
@@ -819,11 +842,11 @@ python-versions = "*"
[[package]]
name = "python-dotenv"
-version = "0.19.0"
+version = "0.21.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
[package.extras]
cli = ["click (>=5.0)"]
@@ -924,11 +947,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sniffio"
-version = "1.2.0"
+version = "1.3.0"
description = "Sniff out which async library your code is running under"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
[[package]]
name = "SQLAlchemy"
@@ -1009,7 +1032,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-protobuf"
-version = "3.20.4"
+version = "3.20.4.1"
description = "Typing stubs for protobuf"
category = "dev"
optional = false
@@ -1107,20 +1130,20 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "zipp"
-version = "3.5.0"
+version = "3.9.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.extras]
-docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
-testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "8825e973ce84c5cf75cf5180e8b6519043265e0633154b3237bb741866a8c4b5"
+content-hash = "7de5e4d432bff49de536b1c90082a6a0821533b3d0fa9d92c22ccaa758d1a65f"
[metadata.files]
aiofiles = [
@@ -1128,8 +1151,8 @@ aiofiles = [
{file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"},
]
anyio = [
- {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
- {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
+ {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
+ {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
]
asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
@@ -1144,8 +1167,8 @@ async-timeout = [
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
]
attrs = [
- {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
- {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
+ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
base58 = [
{file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"},
@@ -1183,69 +1206,86 @@ black = [
{file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
{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.4.2.tar.gz", hash = "sha256:97564481501cbe163e6be4d3cdd0d52d2841e15b830a0185c3c329657e4b8c36"},
+]
Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
certifi = [
- {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
- {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
+ {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
+ {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
cffi = [
- {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
- {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
- {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
- {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
- {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
- {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
- {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
- {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
- {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
- {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
- {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
- {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
- {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
- {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
- {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
- {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
- {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
- {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
- {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
- {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
- {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
- {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
- {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
- {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
- {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
- {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
- {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
- {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
- {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
- {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+ {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+ {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+ {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+ {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+ {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+ {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+ {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+ {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+ {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+ {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+ {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+ {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+ {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+ {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+ {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+ {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
charset-normalizer = [
- {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
- {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
+ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
+ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
]
click = [
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
@@ -1386,51 +1426,51 @@ fastapi = [
{file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
]
grpcio = [
- {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
- {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"},
- {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"},
- {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"},
- {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"},
- {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"},
- {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"},
- {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"},
- {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"},
- {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"},
- {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"},
- {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"},
- {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"},
- {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"},
- {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"},
- {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"},
- {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"},
- {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"},
- {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"},
- {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"},
- {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"},
- {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"},
- {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"},
- {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"},
- {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"},
- {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"},
- {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"},
- {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"},
- {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"},
- {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"},
- {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"},
- {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"},
- {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"},
- {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"},
- {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"},
- {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"},
- {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"},
+ {file = "grpcio-1.50.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:906f4d1beb83b3496be91684c47a5d870ee628715227d5d7c54b04a8de802974"},
+ {file = "grpcio-1.50.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:2d9fd6e38b16c4d286a01e1776fdf6c7a4123d99ae8d6b3f0b4a03a34bf6ce45"},
+ {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:4b123fbb7a777a2fedec684ca0b723d85e1d2379b6032a9a9b7851829ed3ca9a"},
+ {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2f77a90ba7b85bfb31329f8eab9d9540da2cf8a302128fb1241d7ea239a5469"},
+ {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eea18a878cffc804506d39c6682d71f6b42ec1c151d21865a95fae743fda500"},
+ {file = "grpcio-1.50.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b71916fa8f9eb2abd93151fafe12e18cebb302686b924bd4ec39266211da525"},
+ {file = "grpcio-1.50.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:95ce51f7a09491fb3da8cf3935005bff19983b77c4e9437ef77235d787b06842"},
+ {file = "grpcio-1.50.0-cp310-cp310-win32.whl", hash = "sha256:f7025930039a011ed7d7e7ef95a1cb5f516e23c5a6ecc7947259b67bea8e06ca"},
+ {file = "grpcio-1.50.0-cp310-cp310-win_amd64.whl", hash = "sha256:05f7c248e440f538aaad13eee78ef35f0541e73498dd6f832fe284542ac4b298"},
+ {file = "grpcio-1.50.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:ca8a2254ab88482936ce941485c1c20cdeaef0efa71a61dbad171ab6758ec998"},
+ {file = "grpcio-1.50.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3b611b3de3dfd2c47549ca01abfa9bbb95937eb0ea546ea1d762a335739887be"},
+ {file = "grpcio-1.50.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a4cd8cb09d1bc70b3ea37802be484c5ae5a576108bad14728f2516279165dd7"},
+ {file = "grpcio-1.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:156f8009e36780fab48c979c5605eda646065d4695deea4cfcbcfdd06627ddb6"},
+ {file = "grpcio-1.50.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de411d2b030134b642c092e986d21aefb9d26a28bf5a18c47dd08ded411a3bc5"},
+ {file = "grpcio-1.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d144ad10eeca4c1d1ce930faa105899f86f5d99cecfe0d7224f3c4c76265c15e"},
+ {file = "grpcio-1.50.0-cp311-cp311-win32.whl", hash = "sha256:92d7635d1059d40d2ec29c8bf5ec58900120b3ce5150ef7414119430a4b2dd5c"},
+ {file = "grpcio-1.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce8513aee0af9c159319692bfbf488b718d1793d764798c3d5cff827a09e25ef"},
+ {file = "grpcio-1.50.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:8e8999a097ad89b30d584c034929f7c0be280cd7851ac23e9067111167dcbf55"},
+ {file = "grpcio-1.50.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:a50a1be449b9e238b9bd43d3857d40edf65df9416dea988929891d92a9f8a778"},
+ {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:cf151f97f5f381163912e8952eb5b3afe89dec9ed723d1561d59cabf1e219a35"},
+ {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a23d47f2fc7111869f0ff547f771733661ff2818562b04b9ed674fa208e261f4"},
+ {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84d04dec64cc4ed726d07c5d17b73c343c8ddcd6b59c7199c801d6bbb9d9ed1"},
+ {file = "grpcio-1.50.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:67dd41a31f6fc5c7db097a5c14a3fa588af54736ffc174af4411d34c4f306f68"},
+ {file = "grpcio-1.50.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d4c8e73bf20fb53fe5a7318e768b9734cf122fe671fcce75654b98ba12dfb75"},
+ {file = "grpcio-1.50.0-cp37-cp37m-win32.whl", hash = "sha256:7489dbb901f4fdf7aec8d3753eadd40839c9085967737606d2c35b43074eea24"},
+ {file = "grpcio-1.50.0-cp37-cp37m-win_amd64.whl", hash = "sha256:531f8b46f3d3db91d9ef285191825d108090856b3bc86a75b7c3930f16ce432f"},
+ {file = "grpcio-1.50.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:d534d169673dd5e6e12fb57cc67664c2641361e1a0885545495e65a7b761b0f4"},
+ {file = "grpcio-1.50.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:1d8d02dbb616c0a9260ce587eb751c9c7dc689bc39efa6a88cc4fa3e9c138a7b"},
+ {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:baab51dcc4f2aecabf4ed1e2f57bceab240987c8b03533f1cef90890e6502067"},
+ {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40838061e24f960b853d7bce85086c8e1b81c6342b1f4c47ff0edd44bbae2722"},
+ {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:931e746d0f75b2a5cff0a1197d21827a3a2f400c06bace036762110f19d3d507"},
+ {file = "grpcio-1.50.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15f9e6d7f564e8f0776770e6ef32dac172c6f9960c478616c366862933fa08b4"},
+ {file = "grpcio-1.50.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a4c23e54f58e016761b576976da6a34d876420b993f45f66a2bfb00363ecc1f9"},
+ {file = "grpcio-1.50.0-cp38-cp38-win32.whl", hash = "sha256:3e4244c09cc1b65c286d709658c061f12c61c814be0b7030a2d9966ff02611e0"},
+ {file = "grpcio-1.50.0-cp38-cp38-win_amd64.whl", hash = "sha256:8e69aa4e9b7f065f01d3fdcecbe0397895a772d99954bb82eefbb1682d274518"},
+ {file = "grpcio-1.50.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:af98d49e56605a2912cf330b4627e5286243242706c3a9fa0bcec6e6f68646fc"},
+ {file = "grpcio-1.50.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:080b66253f29e1646ac53ef288c12944b131a2829488ac3bac8f52abb4413c0d"},
+ {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:ab5d0e3590f0a16cb88de4a3fa78d10eb66a84ca80901eb2c17c1d2c308c230f"},
+ {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb11464f480e6103c59d558a3875bd84eed6723f0921290325ebe97262ae1347"},
+ {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e07fe0d7ae395897981d16be61f0db9791f482f03fee7d1851fe20ddb4f69c03"},
+ {file = "grpcio-1.50.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d75061367a69808ab2e84c960e9dce54749bcc1e44ad3f85deee3a6c75b4ede9"},
+ {file = "grpcio-1.50.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ae23daa7eda93c1c49a9ecc316e027ceb99adbad750fbd3a56fa9e4a2ffd5ae0"},
+ {file = "grpcio-1.50.0-cp39-cp39-win32.whl", hash = "sha256:177afaa7dba3ab5bfc211a71b90da1b887d441df33732e94e26860b3321434d9"},
+ {file = "grpcio-1.50.0-cp39-cp39-win_amd64.whl", hash = "sha256:ea8ccf95e4c7e20419b7827aa5b6da6f02720270686ac63bd3493a651830235c"},
+ {file = "grpcio-1.50.0.tar.gz", hash = "sha256:12b479839a5e753580b5e6053571de14006157f2ef9b71f38c56dc9b23b95ad6"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -1481,12 +1521,12 @@ httpx = [
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
]
idna = [
- {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
- {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
- {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+ {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
+ {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1580,8 +1620,8 @@ MarkupSafe = [
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
marshmallow = [
- {file = "marshmallow-3.17.0-py3-none-any.whl", hash = "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb"},
- {file = "marshmallow-3.17.0.tar.gz", hash = "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"},
+ {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"},
+ {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"},
]
mock = [
{file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"},
@@ -1617,8 +1657,8 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
outcome = [
- {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
- {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
+ {file = "outcome-1.2.0-py2.py3-none-any.whl", hash = "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"},
+ {file = "outcome-1.2.0.tar.gz", hash = "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
@@ -1641,20 +1681,20 @@ pluggy = [
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
protobuf = [
- {file = "protobuf-4.21.7-cp310-abi3-win32.whl", hash = "sha256:c7cb105d69a87416bd9023e64324e1c089593e6dae64d2536f06bcbe49cd97d8"},
- {file = "protobuf-4.21.7-cp310-abi3-win_amd64.whl", hash = "sha256:3ec85328a35a16463c6f419dbce3c0fc42b3e904d966f17f48bae39597c7a543"},
- {file = "protobuf-4.21.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:db9056b6a11cb5131036d734bcbf91ef3ef9235d6b681b2fc431cbfe5a7f2e56"},
- {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ca200645d6235ce0df3ccfdff1567acbab35c4db222a97357806e015f85b5744"},
- {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b019c79e23a80735cc8a71b95f76a49a262f579d6b84fd20a0b82279f40e2cc1"},
- {file = "protobuf-4.21.7-cp37-cp37m-win32.whl", hash = "sha256:d3f89ccf7182293feba2de2739c8bf34fed1ed7c65a5cf987be00311acac57c1"},
- {file = "protobuf-4.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:a74d96cd960b87b4b712797c741bb3ea3a913f5c2dc4b6cbe9c0f8360b75297d"},
- {file = "protobuf-4.21.7-cp38-cp38-win32.whl", hash = "sha256:8e09d1916386eca1ef1353767b6efcebc0a6859ed7f73cb7fb974feba3184830"},
- {file = "protobuf-4.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:9e355f2a839d9930d83971b9f562395e13493f0e9211520f8913bd11efa53c02"},
- {file = "protobuf-4.21.7-cp39-cp39-win32.whl", hash = "sha256:f370c0a71712f8965023dd5b13277444d3cdfecc96b2c778b0e19acbfd60df6e"},
- {file = "protobuf-4.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:9643684232b6b340b5e63bb69c9b4904cdd39e4303d498d1a92abddc7e895b7f"},
- {file = "protobuf-4.21.7-py2.py3-none-any.whl", hash = "sha256:8066322588d4b499869bf9f665ebe448e793036b552f68c585a9b28f1e393f66"},
- {file = "protobuf-4.21.7-py3-none-any.whl", hash = "sha256:58b81358ec6c0b5d50df761460ae2db58405c063fd415e1101209221a0a810e1"},
- {file = "protobuf-4.21.7.tar.gz", hash = "sha256:71d9dba03ed3432c878a801e2ea51e034b0ea01cf3a4344fb60166cb5f6c8757"},
+ {file = "protobuf-4.21.8-cp310-abi3-win32.whl", hash = "sha256:c252c55ee15175aa1b21b7b9896e6add5162d066d5202e75c39f96136f08cce3"},
+ {file = "protobuf-4.21.8-cp310-abi3-win_amd64.whl", hash = "sha256:809ca0b225d3df42655a12f311dd0f4148a943c51f1ad63c38343e457492b689"},
+ {file = "protobuf-4.21.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bbececaf3cfea9ea65ebb7974e6242d310d2a7772a6f015477e0d79993af4511"},
+ {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:b02eabb9ebb1a089ed20626a90ad7a69cee6bcd62c227692466054b19c38dd1f"},
+ {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:4761201b93e024bb70ee3a6a6425d61f3152ca851f403ba946fb0cde88872661"},
+ {file = "protobuf-4.21.8-cp37-cp37m-win32.whl", hash = "sha256:f2d55ff22ec300c4d954d3b0d1eeb185681ec8ad4fbecff8a5aee6a1cdd345ba"},
+ {file = "protobuf-4.21.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c5f94911dd8feb3cd3786fc90f7565c9aba7ce45d0f254afd625b9628f578c3f"},
+ {file = "protobuf-4.21.8-cp38-cp38-win32.whl", hash = "sha256:b37b76efe84d539f16cba55ee0036a11ad91300333abd213849cbbbb284b878e"},
+ {file = "protobuf-4.21.8-cp38-cp38-win_amd64.whl", hash = "sha256:2c92a7bfcf4ae76a8ac72e545e99a7407e96ffe52934d690eb29a8809ee44d7b"},
+ {file = "protobuf-4.21.8-cp39-cp39-win32.whl", hash = "sha256:89d641be4b5061823fa0e463c50a2607a97833e9f8cfb36c2f91ef5ccfcc3861"},
+ {file = "protobuf-4.21.8-cp39-cp39-win_amd64.whl", hash = "sha256:bc471cf70a0f53892fdd62f8cd4215f0af8b3f132eeee002c34302dff9edd9b6"},
+ {file = "protobuf-4.21.8-py2.py3-none-any.whl", hash = "sha256:a55545ce9eec4030cf100fcb93e861c622d927ef94070c1a3c01922902464278"},
+ {file = "protobuf-4.21.8-py3-none-any.whl", hash = "sha256:0f236ce5016becd989bf39bd20761593e6d8298eccd2d878eda33012645dc369"},
+ {file = "protobuf-4.21.8.tar.gz", hash = "sha256:427426593b55ff106c84e4a88cac855175330cb6eb7e889e85aaa7b5652b686d"},
]
psycopg2-binary = [
{file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
@@ -1817,8 +1857,8 @@ python-bitcoinlib = [
{file = "python_bitcoinlib-0.11.2-py3-none-any.whl", hash = "sha256:78bd4ee717fe805cd760dfdd08765e77b7c7dbef4627f8596285e84953756508"},
]
python-dotenv = [
- {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
- {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+ {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
+ {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
]
PyYAML = [
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
@@ -1901,8 +1941,8 @@ six = [
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sniffio = [
- {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
- {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
+ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
]
SQLAlchemy = [
{file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"},
@@ -1982,8 +2022,8 @@ typed-ast = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
]
types-protobuf = [
- {file = "types-protobuf-3.20.4.tar.gz", hash = "sha256:0dad3a5009895c985a56e2837f61902bad9594151265ac0ee907bb16d0b01eb7"},
- {file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"},
+ {file = "types-protobuf-3.20.4.1.tar.gz", hash = "sha256:67df7cc7ec85d114db2664a8ae8905543e75fb5edbed437dabc9e9fb8f8fcf9e"},
+ {file = "types_protobuf-3.20.4.1-py3-none-any.whl", hash = "sha256:c227975ffd0f6a1eb1754e9a3aa9ca3b12265e63b462e9761e824c41fd25331c"},
]
typing-extensions = [
{file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
@@ -2055,6 +2095,6 @@ win32-setctime = [
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
zipp = [
- {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
- {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+ {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"},
+ {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"},
]
diff --git a/pyproject.toml b/pyproject.toml
index 37fd7aed..24c041d7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,11 +12,11 @@ script = "build.py"
python = "^3.10 | ^3.9 | ^3.8 | ^3.7"
aiofiles = "0.8.0"
asgiref = "3.4.1"
-attrs = "21.2.0"
+attrs = "22.1.0"
bech32 = "1.2.0"
bitstring = "3.1.9"
-certifi = "2021.5.30"
-charset-normalizer = "2.0.6"
+certifi = "2022.9.24"
+charset-normalizer = "2.0.12"
click = "8.0.4"
ecdsa = "0.18.0"
embit = "0.4.9"
@@ -26,27 +26,27 @@ h11 = "0.12.0"
httpcore = "0.15.0"
httptools = "0.4.0"
httpx = "0.23.0"
-idna = "3.2"
-importlib-metadata = "4.8.1"
+idna = "3.4"
+importlib-metadata = "5.0.0"
jinja2 = "3.0.1"
lnurl = "0.3.6"
markupsafe = "2.0.1"
-marshmallow = "3.17.0"
-outcome = "1.1.0"
+marshmallow = "3.18.0"
+outcome = "1.2.0"
psycopg2-binary = "2.9.1"
pycryptodomex = "3.14.1"
pydantic = "1.10.2"
pypng = "0.0.21"
pyqrcode = "1.2.1"
pyScss = "1.4.0"
-python-dotenv = "0.19.0"
+python-dotenv = "0.21.0"
pyyaml = "5.4.1"
represent = "1.6.0.post0"
rfc3986 = "1.5.0"
secp256k1 = "0.14.0"
shortuuid = "1.0.1"
six = "1.16.0"
-sniffio = "1.2.0"
+sniffio = "1.3.0"
sqlalchemy = "1.3.24"
sqlalchemy-aio = "0.17.0"
sse-starlette = "0.6.2"
@@ -55,16 +55,16 @@ uvicorn = "0.18.3"
uvloop = "0.16.0"
watchgod = "0.7"
websockets = "10.0"
-zipp = "3.5.0"
+zipp = "3.9.0"
loguru = "0.6.0"
-cffi = "1.15.0"
+cffi = "1.15.1"
websocket-client = "1.3.3"
grpcio = "^1.49.1"
protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
-cashu = {path = "../cashu"}
+cashu = "0.4.2"
[tool.poetry.dev-dependencies]
From ea58ae77ef05e4f36af46a4e4c9b7a42b743471c Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 21 Oct 2022 15:17:44 +0200
Subject: [PATCH 0195/1058] add cashu to requirements.txt
---
requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/requirements.txt b/requirements.txt
index eb9a6e5e..6a48c861 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ asyncio==3.4.3
attrs==21.4.0
bech32==1.2.0
bitstring==3.1.9
+cashu==0.4.2
cerberus==1.3.4
certifi==2022.6.15
cffi==1.15.0
From a9b4dd251f05586751ac06efeb8fe81a2f0dc3b3 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 21 Oct 2022 15:23:44 +0200
Subject: [PATCH 0196/1058] generate requirements from poetry
---
requirements.txt | 138 ++++++++++++++++++++++++++++-------------------
1 file changed, 82 insertions(+), 56 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 6a48c861..e8001e70 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,56 +1,82 @@
-aiofiles==0.8.0
-anyio==3.6.1
-asyncio==3.4.3
-attrs==21.4.0
-bech32==1.2.0
-bitstring==3.1.9
-cashu==0.4.2
-cerberus==1.3.4
-certifi==2022.6.15
-cffi==1.15.0
-click==8.1.3
-ecdsa==0.18.0
-embit==0.5.0
-environs==9.5.0
-fastapi==0.79.0
-h11==0.12.0
-httpcore==0.15.0
-httptools==0.4.0
-httpx==0.23.0
-idna==3.3
-jinja2==3.0.1
-lnurl==0.3.6
-loguru==0.6.0
-markupsafe==2.1.1
-marshmallow==3.17.0
-outcome==1.2.0
-packaging==21.3
-psycopg2-binary==2.9.3
-pycparser==2.21
-pycryptodomex==3.15.0
-pydantic==1.9.1
-pyngrok==5.1.0
-pyparsing==3.0.9
-pypng==0.20220715.0
-pyqrcode==1.2.1
-pyscss==1.4.0
-python-dotenv==0.20.0
-pyyaml==6.0
-represent==1.6.0.post0
-rfc3986==1.5.0
-secp256k1==0.14.0
-shortuuid==1.0.9
-six==1.16.0
-sniffio==1.2.0
-sqlalchemy-aio==0.17.0
-sqlalchemy==1.3.23
-sse-starlette==0.10.3
-starlette==0.19.1
-typing-extensions==4.3.0
-uvicorn==0.18.2
-uvloop==0.16.0
-watchfiles==0.16.0
-websockets==10.3
-websocket-client==1.3.3
-async-timeout==4.0.2
-setuptools==65.4.0
\ No newline at end of file
+aiofiles==0.8.0 ; python_version >= "3.7" and python_version < "4.0"
+anyio==3.6.2 ; python_version >= "3.7" and python_version < "4.0"
+asgiref==3.4.1 ; python_version >= "3.7" and python_version < "4.0"
+asn1crypto==1.5.1 ; python_version >= "3.7" and python_version < "4.0"
+async-timeout==4.0.2 ; python_version >= "3.7" and python_version < "4.0"
+attrs==22.1.0 ; python_version >= "3.7" and python_version < "4.0"
+base58==2.1.1 ; python_version >= "3.7" and python_version < "4.0"
+bech32==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
+bitstring==3.1.9 ; python_version >= "3.7" and python_version < "4.0"
+cashu==0.4.2 ; python_version >= "3.7" and python_version < "4.0"
+cerberus==1.3.4 ; python_version >= "3.7" and python_version < "4.0"
+certifi==2022.9.24 ; python_version >= "3.7" and python_version < "4.0"
+cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0"
+charset-normalizer==2.0.12 ; python_version >= "3.7" and python_version < "4.0"
+click==8.0.4 ; python_version >= "3.7" and python_version < "4.0"
+coincurve==17.0.0 ; python_version >= "3.7" and python_version < "4.0"
+colorama==0.4.5 ; python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows" or python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
+cryptography==36.0.2 ; python_version >= "3.7" and python_version < "4.0"
+ecdsa==0.18.0 ; python_version >= "3.7" and python_version < "4.0"
+embit==0.4.9 ; python_version >= "3.7" and python_version < "4.0"
+enum34==1.1.10 ; python_version >= "3.7" and python_version < "4.0"
+environs==9.5.0 ; python_version >= "3.7" and python_version < "4.0"
+fastapi==0.83.0 ; python_version >= "3.7" and python_version < "4.0"
+grpcio==1.50.0 ; python_version >= "3.7" and python_version < "4.0"
+h11==0.12.0 ; python_version >= "3.7" and python_version < "4.0"
+httpcore==0.15.0 ; python_version >= "3.7" and python_version < "4.0"
+httptools==0.4.0 ; python_version >= "3.7" and python_version < "4.0"
+httpx==0.23.0 ; python_version >= "3.7" and python_version < "4.0"
+idna==3.4 ; python_version >= "3.7" and python_version < "4.0"
+importlib-metadata==5.0.0 ; python_version >= "3.7" and python_version < "4.0"
+iniconfig==1.1.1 ; python_version >= "3.7" and python_version < "4.0"
+jinja2==3.0.1 ; python_version >= "3.7" and python_version < "4.0"
+lnurl==0.3.6 ; python_version >= "3.7" and python_version < "4.0"
+loguru==0.6.0 ; python_version >= "3.7" and python_version < "4.0"
+markupsafe==2.0.1 ; python_version >= "3.7" and python_version < "4.0"
+marshmallow==3.18.0 ; python_version >= "3.7" and python_version < "4.0"
+outcome==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
+packaging==21.3 ; python_version >= "3.7" and python_version < "4.0"
+pathlib2==2.3.7.post1 ; python_version >= "3.7" and python_version < "4.0"
+pluggy==1.0.0 ; python_version >= "3.7" and python_version < "4.0"
+protobuf==4.21.8 ; python_version >= "3.7" and python_version < "4.0"
+psycopg2-binary==2.9.1 ; python_version >= "3.7" and python_version < "4.0"
+py==1.11.0 ; python_version >= "3.7" and python_version < "4.0"
+pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0"
+pycryptodomex==3.14.1 ; python_version >= "3.7" and python_version < "4.0"
+pydantic==1.10.2 ; python_version >= "3.7" and python_version < "4.0"
+pyln-bolt7==1.0.246 ; python_version >= "3.7" and python_version < "4.0"
+pyln-client==0.11.1 ; python_version >= "3.7" and python_version < "4.0"
+pyln-proto==0.11.1 ; python_version >= "3.7" and python_version < "4.0"
+pyparsing==3.0.9 ; python_version >= "3.7" and python_version < "4.0"
+pypng==0.0.21 ; python_version >= "3.7" and python_version < "4.0"
+pyqrcode==1.2.1 ; python_version >= "3.7" and python_version < "4.0"
+pyscss==1.4.0 ; python_version >= "3.7" and python_version < "4.0"
+pysocks==1.7.1 ; python_version >= "3.7" and python_version < "4.0"
+pytest-asyncio==0.19.0 ; python_version >= "3.7" and python_version < "4.0"
+pytest==7.1.3 ; python_version >= "3.7" and python_version < "4.0"
+python-bitcoinlib==0.11.2 ; python_version >= "3.7" and python_version < "4.0"
+python-dotenv==0.21.0 ; python_version >= "3.7" and python_version < "4.0"
+pyyaml==5.4.1 ; python_version >= "3.7" and python_version < "4.0"
+represent==1.6.0.post0 ; python_version >= "3.7" and python_version < "4.0"
+requests==2.27.1 ; python_version >= "3.7" and python_version < "4.0"
+rfc3986==1.5.0 ; python_version >= "3.7" and python_version < "4.0"
+rfc3986[idna2008]==1.5.0 ; python_version >= "3.7" and python_version < "4.0"
+secp256k1==0.14.0 ; python_version >= "3.7" and python_version < "4.0"
+setuptools==65.5.0 ; python_version >= "3.7" and python_version < "4.0"
+shortuuid==1.0.1 ; python_version >= "3.7" and python_version < "4.0"
+six==1.16.0 ; python_version >= "3.7" and python_version < "4.0"
+sniffio==1.3.0 ; python_version >= "3.7" and python_version < "4.0"
+sqlalchemy-aio==0.17.0 ; python_version >= "3.7" and python_version < "4.0"
+sqlalchemy==1.3.24 ; python_version >= "3.7" and python_version < "4.0"
+sse-starlette==0.6.2 ; python_version >= "3.7" and python_version < "4.0"
+starlette==0.19.1 ; python_version >= "3.7" and python_version < "4.0"
+tomli==2.0.1 ; python_version >= "3.7" and python_version < "4.0"
+typing-extensions==4.4.0 ; python_version >= "3.7" and python_version < "4.0"
+urllib3==1.26.12 ; python_version >= "3.7" and python_version < "4"
+uvicorn==0.18.3 ; python_version >= "3.7" and python_version < "4.0"
+uvloop==0.16.0 ; python_version >= "3.7" and python_version < "4.0"
+watchgod==0.7 ; python_version >= "3.7" and python_version < "4.0"
+websocket-client==1.3.3 ; python_version >= "3.7" and python_version < "4.0"
+websockets==10.0 ; python_version >= "3.7" and python_version < "4.0"
+win32-setctime==1.1.0 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
+zipp==3.9.0 ; python_version >= "3.7" and python_version < "4.0"
From 7e38d899af366e1c17954eb201c18460620cd169 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 21 Oct 2022 15:31:24 +0200
Subject: [PATCH 0197/1058] make format
---
lnbits/extensions/cashu/__init__.py | 2 +-
lnbits/extensions/cashu/crud.py | 8 +--
lnbits/extensions/cashu/tasks.py | 8 +--
.../cashu/templates/cashu/_cashu.html | 4 +-
lnbits/extensions/cashu/views_api.py | 68 ++++++++-----------
5 files changed, 40 insertions(+), 50 deletions(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index 440be092..7944e658 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -34,7 +34,7 @@ def cashu_renderer():
return template_renderer(["lnbits/extensions/cashu/templates"])
-from .tasks import wait_for_paid_invoices, startup_cashu_mint
+from .tasks import startup_cashu_mint, wait_for_paid_invoices
from .views import * # noqa
from .views_api import * # noqa
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
index 7ecf9b6a..cbeedc12 100644
--- a/lnbits/extensions/cashu/crud.py
+++ b/lnbits/extensions/cashu/crud.py
@@ -2,21 +2,19 @@ import os
import random
import time
from binascii import hexlify, unhexlify
-from typing import List, Optional, Union, Any
+from typing import Any, List, Optional, Union
+from cashu.core.base import MintKeyset
from embit import bip32, bip39, ec, script
from embit.networks import NETWORKS
from loguru import logger
+from lnbits.db import Connection, Database
from lnbits.helpers import urlsafe_short_hash
from . import db
-
from .models import Cashu, Pegs, Promises, Proof
-from cashu.core.base import MintKeyset
-from lnbits.db import Database, Connection
-
async def create_cashu(
cashu_id: str, keyset_id: str, wallet_id: str, data: Cashu
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
index dddd1ef1..40b521f0 100644
--- a/lnbits/extensions/cashu/tasks.py
+++ b/lnbits/extensions/cashu/tasks.py
@@ -1,14 +1,14 @@
import asyncio
import json
+from cashu.core.migrations import migrate_databases
+from cashu.mint import migrations
+
from lnbits.core.models import Payment
from lnbits.tasks import register_invoice_listener
-from .crud import get_cashu
-
-from cashu.mint import migrations
-from cashu.core.migrations import migrate_databases
from . import db, ledger
+from .crud import get_cashu
async def startup_cashu_mint():
diff --git a/lnbits/extensions/cashu/templates/cashu/_cashu.html b/lnbits/extensions/cashu/templates/cashu/_cashu.html
index 03457ac7..0c7c4338 100644
--- a/lnbits/extensions/cashu/templates/cashu/_cashu.html
+++ b/lnbits/extensions/cashu/templates/cashu/_cashu.html
@@ -7,7 +7,9 @@
Created by
- arcbtc , vlad , calle . arcbtc,
+ vlad ,
+ calle .
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index b271bccc..28857663 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -1,61 +1,51 @@
import json
-from http import HTTPStatus
-from typing import Union
import math
+from http import HTTPStatus
from typing import Dict, List, Union
import httpx
-from fastapi import Query
-from fastapi.params import Depends
-from lnurl import decode as decode_lnurl
-from loguru import logger
-from secp256k1 import PublicKey
-from starlette.exceptions import HTTPException
-from lnbits import bolt11
-
-from lnbits.core.crud import get_user
-from lnbits.core.services import (
- check_transaction_status,
- create_invoice,
- fee_reserve,
- pay_invoice,
-)
-
-from lnbits.core.views.api import api_payment
-from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.wallets.base import PaymentStatus
-from lnbits.helpers import urlsafe_short_hash
-from lnbits.core.crud import check_internal
-
-# --------- extension imports
-
-from . import cashu_ext
-from .crud import (
- create_cashu,
- delete_cashu,
- get_cashu,
- get_cashus,
-)
-
-from .models import Cashu
-
-from . import ledger
# -------- cashu imports
from cashu.core.base import (
- Proof,
BlindedSignature,
CheckFeesRequest,
CheckFeesResponse,
CheckRequest,
GetMeltResponse,
GetMintResponse,
+ Invoice,
MeltRequest,
MintRequest,
PostSplitResponse,
+ Proof,
SplitRequest,
- Invoice,
)
+from fastapi import Query
+from fastapi.params import Depends
+from lnurl import decode as decode_lnurl
+from loguru import logger
+from secp256k1 import PublicKey
+from starlette.exceptions import HTTPException
+
+from lnbits import bolt11
+from lnbits.core.crud import check_internal, get_user
+from lnbits.core.services import (
+ check_transaction_status,
+ create_invoice,
+ fee_reserve,
+ pay_invoice,
+)
+from lnbits.core.views.api import api_payment
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.wallets.base import PaymentStatus
+
+from . import cashu_ext, ledger
+from .crud import create_cashu, delete_cashu, get_cashu, get_cashus
+from .models import Cashu
+
+# --------- extension imports
+
LIGHTNING = False
From e8e4c7f9c3ee16c28b0a7e6cee65a958b0110db9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 24 Oct 2022 09:26:32 +0200
Subject: [PATCH 0198/1058] works
---
lnbits/extensions/cashu/migrations.py | 3 +-
.../cashu/templates/cashu/_api_docs.html | 12 +-
.../cashu/templates/cashu/index.html | 8 +-
.../cashu/templates/cashu/wallet.html | 12 +-
lnbits/extensions/cashu/views_api.py | 119 ++++++++++++------
poetry.lock | 75 ++++-------
pyproject.toml | 2 +-
7 files changed, 125 insertions(+), 106 deletions(-)
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
index ec520765..b54c4108 100644
--- a/lnbits/extensions/cashu/migrations.py
+++ b/lnbits/extensions/cashu/migrations.py
@@ -12,7 +12,8 @@ async def m001_initial(db):
fraction BOOL,
maxsats INT,
coins INT,
- keyset_id TEXT NOT NULL
+ keyset_id TEXT NOT NULL,
+ issued_sat INT
);
"""
)
diff --git a/lnbits/extensions/cashu/templates/cashu/_api_docs.html b/lnbits/extensions/cashu/templates/cashu/_api_docs.html
index 3476d41a..27ce55f9 100644
--- a/lnbits/extensions/cashu/templates/cashu/_api_docs.html
+++ b/lnbits/extensions/cashu/templates/cashu/_api_docs.html
@@ -8,7 +8,7 @@
- GET /cashu/api/v1/cashus
+ GET /cashu/api/v1/mints
Headers
{"X-Api-Key": <invoice_key>}
Body (application/json)
@@ -18,7 +18,7 @@
[<cashu_object>, ...]
Curl example
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>"
@@ -27,7 +27,7 @@
- POST /cashu/api/v1/cashus
+ POST /cashu/api/v1/mints
Headers
{"X-Api-Key": <invoice_key>}
Body (application/json)
@@ -43,7 +43,7 @@
>
Curl example
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:
application/json" -H "X-Api-Key: <admin_key>"
@@ -62,7 +62,7 @@
DELETE
- /cashu/api/v1/cashus/<cashu_id>
Headers
{"X-Api-Key": <admin_key>}
@@ -71,7 +71,7 @@
Curl example
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>"
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html
index dcb2b4a7..20ed567c 100644
--- a/lnbits/extensions/cashu/templates/cashu/index.html
+++ b/lnbits/extensions/cashu/templates/cashu/index.html
@@ -271,7 +271,7 @@
LNbits.api
.request(
'GET',
- '/cashu/api/v1/cashus?all_wallets=true',
+ '/cashu/api/v1/mints?all_wallets=true',
this.g.user.wallets[0].inkey
)
.then(function (response) {
@@ -294,7 +294,7 @@
LNbits.api
.request(
'POST',
- '/cashu/api/v1/cashus',
+ '/cashu/api/v1/mints',
_.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet})
.inkey,
data
@@ -314,13 +314,13 @@
LNbits.utils
.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 () {
LNbits.api
.request(
'DELETE',
- '/cashu/api/v1/cashus/' + cashuId,
+ '/cashu/api/v1/mints/' + cashuId,
_.findWhere(self.g.user.wallets, {id: cashu.wallet}).adminkey
)
.then(function (response) {
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 4f12cd1c..11ac044b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -913,7 +913,7 @@ page_container %}
try {
const {data} = await LNbits.api.request(
'GET',
- `/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.invoiceData.amount}`
+ `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}`
)
console.log('### data', data)
@@ -940,7 +940,7 @@ page_container %}
try {
const {data} = await LNbits.api.request(
'POST',
- `/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${invoice.hash}`,
+ `/cashu/api/v1/${this.mintId}/mint?payment_hash=${invoice.hash}`,
'',
{
blinded_messages: []
@@ -991,7 +991,7 @@ page_container %}
try {
const {data} = await LNbits.api.request(
'POST',
- `/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${hash}`,
+ `/cashu/api/v1/${this.mintId}/mint?payment_hash=${hash}`,
'',
{
blinded_messages: blindedMessages
@@ -1086,7 +1086,7 @@ page_container %}
try {
const {data} = await LNbits.api.request(
'POST',
- `/cashu/api/v1/cashu/${this.mintId}/split`,
+ `/cashu/api/v1/${this.mintId}/split`,
'',
payload
)
@@ -1221,7 +1221,7 @@ page_container %}
try {
const {data} = await LNbits.api.request(
'POST',
- `/cashu/api/v1/cashu/${this.mintId}/melt`,
+ `/cashu/api/v1/${this.mintId}/melt`,
'',
payload
)
@@ -1257,7 +1257,7 @@ page_container %}
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
'GET',
- `/cashu/api/v1/cashu/${this.mintId}/keys`
+ `/cashu/api/v1/${this.mintId}/keys`
)
this.keys = data
localStorage.setItem('cashu.keys', JSON.stringify(data))
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 28857663..d4654c55 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -47,17 +47,20 @@ from .models import Cashu
# --------- extension imports
-LIGHTNING = False
+LIGHTNING = True
########################################
############### 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(
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
+ """
+ Get all mints of this wallet.
+ """
wallet_ids = [wallet.wallet.id]
if all_wallets:
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)]
-@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)):
+ """
+ Create a new mint for this wallet.
+ """
cashu_id = urlsafe_short_hash()
# generate a new keyset in cashu
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()
+@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_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]:
"""Get the public keys of the mint"""
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)
-@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:
"""
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
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint")
+@cashu_ext.post("/api/v1/{cashu_id}/mint")
async def mint_coins(
data: MintRequest,
cashu_id: str = Query(None),
@@ -157,7 +199,7 @@ async def mint_coins(
if invoice is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
- detail="Mint does not have this invoice.",
+ detail="Mint does not know this invoice.",
)
if invoice.issued == True:
raise HTTPException(
@@ -173,27 +215,31 @@ async def mint_coins(
)
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:
- 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
)
+ 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
except Exception as e:
logger.error(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(
payload: MeltRequest, cashu_id: str = Query(None)
) -> GetMeltResponse:
@@ -211,7 +257,7 @@ async def melt_coins(
# 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.",
+ detail="Proofs include tokens from another mint.",
)
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)
-@cashu_ext.post("/api/v1/check")
-async def check_spendable(payload: CheckRequest) -> Dict[int, bool]:
+@cashu_ext.post("/api/v1/{cashu_id}/check")
+async def check_spendable(
+ payload: CheckRequest, cashu_id: str = Query(None)
+) -> Dict[int, bool]:
"""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)
-@cashu_ext.post("/api/v1/checkfees")
-async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
+@cashu_ext.post("/api/v1/{cashu_id}/checkfees")
+async def check_fees(
+ payload: CheckFeesRequest, cashu_id: str = Query(None)
+) -> 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).
"""
+ 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)
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)
-@cashu_ext.post("/api/v1/cashu/{cashu_id}/split")
+@cashu_ext.post("/api/v1/{cashu_id}/split")
async def split(
payload: SplitRequest, cashu_id: str = Query(None)
) -> PostSplitResponse:
@@ -291,7 +351,8 @@ async def split(
assert outputs, Exception("no outputs provided.")
split_return = None
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:
HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -318,24 +379,6 @@ async def split(
# 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)
-
-
# ########################################
# #################????###################
# ########################################
diff --git a/poetry.lock b/poetry.lock
index 9c426798..b87b102f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -124,55 +124,33 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cashu"
version = "0.4.2"
-description = "Ecash wallet and mint with Bitcoin Lightning support"
+description = "Ecash wallet and mint."
category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = "^3.7"
+develop = false
[package.dependencies]
-anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
-ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
-iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-py = {version = "1.11.0", 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\""}
-pyparsing = {version = "3.0.9", 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\""}
-python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
-uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
-zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+bech32 = "^1.2.0"
+bitstring = "^3.1.9"
+click = "8.0.4"
+ecdsa = "^0.18.0"
+environs = "^9.5.0"
+fastapi = "^0.83.0"
+h11 = "0.12.0"
+loguru = "^0.6.0"
+pydantic = "^1.10.2"
+pytest-asyncio = "0.19.0"
+python-bitcoinlib = "^0.11.2"
+requests = "2.27.1"
+secp256k1 = "^0.14.0"
+SQLAlchemy = "1.3.24"
+sqlalchemy-aio = "^0.17.0"
+uvicorn = "^0.18.3"
+
+[package.source]
+type = "directory"
+url = "../cashu"
[[package]]
name = "Cerberus"
@@ -1143,7 +1121,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "7de5e4d432bff49de536b1c90082a6a0821533b3d0fa9d92c22ccaa758d1a65f"
+content-hash = "ded9ab80b3bec75bf904c3f598afbdff5f1577aaedbec25045c3b42c332f8ccc"
[metadata.files]
aiofiles = [
@@ -1206,10 +1184,7 @@ black = [
{file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
-cashu = [
- {file = "cashu-0.4.2-py3-none-any.whl", hash = "sha256:6d24f5e921c33dae1b6823f5e34feab0d6d5662b56a67c29095d48241163a887"},
- {file = "cashu-0.4.2.tar.gz", hash = "sha256:97564481501cbe163e6be4d3cdd0d52d2841e15b830a0185c3c329657e4b8c36"},
-]
+cashu = []
Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
diff --git a/pyproject.toml b/pyproject.toml
index 24c041d7..f1a52348 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,7 +64,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
-cashu = "0.4.2"
+cashu = {path = "../cashu"}
[tool.poetry.dev-dependencies]
From 49c58cc8d0cb48fd874ba40d28c4331a065424df Mon Sep 17 00:00:00 2001
From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com>
Date: Mon, 24 Oct 2022 11:48:34 +0200
Subject: [PATCH 0199/1058] return a reason of failed payment to the pos;
disable a change of wallet in the form
---
lnbits/extensions/boltcards/lnurl.py | 4 ++--
lnbits/extensions/boltcards/templates/boltcards/index.html | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py
index 064bde2c..cd4c6ba4 100644
--- a/lnbits/extensions/boltcards/lnurl.py
+++ b/lnbits/extensions/boltcards/lnurl.py
@@ -136,8 +136,8 @@ async def lnurl_callback(
)
return {"status": "OK"}
- except:
- return {"status": "ERROR", "reason": f"Payment failed"}
+ except Exception as exc:
+ return {"status": "ERROR", "reason": f"Payment failed - {exc}"}
# /boltcards/api/v1/auth?a=00000000000000000000000000000000
diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html
index f795e454..7b9713e2 100644
--- a/lnbits/extensions/boltcards/templates/boltcards/index.html
+++ b/lnbits/extensions/boltcards/templates/boltcards/index.html
@@ -215,6 +215,7 @@
emit-value
v-model="cardDialog.data.wallet"
:options="g.user.walletOptions"
+ :disable="cardDialog.data.id != null"
label="Wallet *"
>
From 708f855c1d485639dfa559c2f2a26a5a9f41c1d7 Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Mar 2022 05:03:32 +0000
Subject: [PATCH 0200/1058] Added old admin extension
---
.env.example | 14 +-
lnbits/extensions/admin/README.md | 11 +
lnbits/extensions/admin/__init__.py | 10 +
lnbits/extensions/admin/config.json | 6 +
lnbits/extensions/admin/crud.py | 59 ++
lnbits/extensions/admin/migrations.py | 256 ++++++++
lnbits/extensions/admin/models.py | 38 ++
.../admin/templates/admin/index.html | 565 ++++++++++++++++++
lnbits/extensions/admin/views.py | 20 +
lnbits/extensions/admin/views_api.py | 41 ++
10 files changed, 1014 insertions(+), 6 deletions(-)
create mode 100644 lnbits/extensions/admin/README.md
create mode 100644 lnbits/extensions/admin/__init__.py
create mode 100644 lnbits/extensions/admin/config.json
create mode 100644 lnbits/extensions/admin/crud.py
create mode 100644 lnbits/extensions/admin/migrations.py
create mode 100644 lnbits/extensions/admin/models.py
create mode 100644 lnbits/extensions/admin/templates/admin/index.html
create mode 100644 lnbits/extensions/admin/views.py
create mode 100644 lnbits/extensions/admin/views_api.py
diff --git a/.env.example b/.env.example
index 987c6ca6..bfaeb515 100644
--- a/.env.example
+++ b/.env.example
@@ -3,17 +3,19 @@ PORT=5000
DEBUG=false
-LNBITS_ALLOWED_USERS=""
-LNBITS_ADMIN_USERS=""
-# Extensions only admin can access
-LNBITS_ADMIN_EXTENSIONS="ngrok"
+LNBITS_ADMIN_USERS="" # User IDs seperated by comma
+LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Extensions only admin can access
+
+LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
+
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
# csv ad image filepaths or urls, extensions can choose to honor
LNBITS_AD_SPACE=""
# Hides wallet api, extensions can choose to honor
-LNBITS_HIDE_API=false
+LNBITS_HIDE_API=false
# Disable extensions for all users, use "all" to disable all extensions
LNBITS_DISABLED_EXTENSIONS="amilk"
@@ -67,7 +69,7 @@ LNBITS_KEY=LNBITS_ADMIN_KEY
LND_REST_ENDPOINT=https://127.0.0.1:8080/
LND_REST_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert"
LND_REST_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon or HEXSTRING"
-# To use an AES-encrypted macaroon, set
+# To use an AES-encrypted macaroon, set
# LND_REST_MACAROON_ENCRYPTED="eNcRyPtEdMaCaRoOn"
# LNPayWallet
diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md
new file mode 100644
index 00000000..27729459
--- /dev/null
+++ b/lnbits/extensions/admin/README.md
@@ -0,0 +1,11 @@
+Example Extension
+*tagline*
+This is an example extension to help you organise and build you own.
+
+Try to include an image
+
+
+
+If your extension has API endpoints, include useful ones here
+
+curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
new file mode 100644
index 00000000..d5f26c90
--- /dev/null
+++ b/lnbits/extensions/admin/__init__.py
@@ -0,0 +1,10 @@
+from quart import Blueprint
+from lnbits.db import Database
+
+db = Database("ext_admin")
+
+admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates")
+
+
+from .views_api import * # noqa
+from .views import * # noqa
diff --git a/lnbits/extensions/admin/config.json b/lnbits/extensions/admin/config.json
new file mode 100644
index 00000000..69661733
--- /dev/null
+++ b/lnbits/extensions/admin/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Admin",
+ "short_description": "Manage your LNbits install",
+ "icon": "build",
+ "contributors": ["benarc"]
+}
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
new file mode 100644
index 00000000..cb8f9b5b
--- /dev/null
+++ b/lnbits/extensions/admin/crud.py
@@ -0,0 +1,59 @@
+from typing import List, Optional
+
+from . import db
+from .models import Admin, Funding
+from lnbits.settings import *
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.core.crud import create_payment
+from lnbits.db import Connection
+
+
+def update_wallet_balance(wallet_id: str, amount: int) -> str:
+ temp_id = f"temp_{urlsafe_short_hash()}"
+ internal_id = f"internal_{urlsafe_short_hash()}"
+ create_payment(
+ wallet_id=wallet_id,
+ checking_id=internal_id,
+ payment_request="admin_internal",
+ payment_hash="admin_internal",
+ amount=amount * 1000,
+ memo="Admin top up",
+ pending=False,
+ )
+ return "success"
+
+
+async def update_admin(
+) -> Optional[Admin]:
+ if not CLightningWallet:
+ print("poo")
+ await db.execute(
+ """
+ UPDATE admin
+ SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ?
+ WHERE 1
+ """,
+ (
+
+ ),
+ )
+ row = await db.fetchone("SELECT * FROM admin WHERE 1")
+ return Admin.from_row(row) if row else None
+
+async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
+ )
+ row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
+ return Jukebox(**row) if row else None
+
+async def get_admin() -> List[Admin]:
+ row = await db.fetchone("SELECT * FROM admin WHERE 1")
+ return Admin.from_row(row) if row else None
+
+
+async def get_funding() -> List[Funding]:
+ rows = await db.fetchall("SELECT * FROM funding")
+
+ return [Funding.from_row(row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
new file mode 100644
index 00000000..82d934cb
--- /dev/null
+++ b/lnbits/extensions/admin/migrations.py
@@ -0,0 +1,256 @@
+from sqlalchemy.exc import OperationalError # type: ignore
+from os import getenv
+from lnbits.helpers import urlsafe_short_hash
+
+
+async def m001_create_admin_table(db):
+ user = None
+ site_title = None
+ site_tagline = None
+ site_description = None
+ allowed_users = None
+ admin_user = None
+ default_wallet_name = None
+ data_folder = None
+ disabled_ext = None
+ force_https = True
+ service_fee = 0
+ funding_source = ""
+
+ if getenv("LNBITS_SITE_TITLE"):
+ site_title = getenv("LNBITS_SITE_TITLE")
+
+ if getenv("LNBITS_SITE_TAGLINE"):
+ site_tagline = getenv("LNBITS_SITE_TAGLINE")
+
+ if getenv("LNBITS_SITE_DESCRIPTION"):
+ site_description = getenv("LNBITS_SITE_DESCRIPTION")
+
+ if getenv("LNBITS_ALLOWED_USERS"):
+ allowed_users = getenv("LNBITS_ALLOWED_USERS")
+
+ if getenv("LNBITS_ADMIN_USER"):
+ admin_user = getenv("LNBITS_ADMIN_USER")
+
+ if getenv("LNBITS_DEFAULT_WALLET_NAME"):
+ default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
+
+ if getenv("LNBITS_DATA_FOLDER"):
+ data_folder = getenv("LNBITS_DATA_FOLDER")
+
+ if getenv("LNBITS_DISABLED_EXTENSIONS"):
+ disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+
+ if getenv("LNBITS_FORCE_HTTPS"):
+ force_https = getenv("LNBITS_FORCE_HTTPS")
+
+ if getenv("LNBITS_SERVICE_FEE"):
+ service_fee = getenv("LNBITS_SERVICE_FEE")
+
+ if getenv("LNBITS_BACKEND_WALLET_CLASS"):
+ funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
+
+ await db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS admin (
+ user TEXT,
+ site_title TEXT,
+ site_tagline TEXT,
+ site_description TEXT,
+ admin_user TEXT,
+ allowed_users TEXT,
+ default_wallet_name TEXT,
+ data_folder TEXT,
+ disabled_ext TEXT,
+ force_https BOOLEAN,
+ service_fee INT,
+ funding_source TEXT
+ );
+ """
+ )
+ await db.execute(
+ """
+ INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ user,
+ site_title,
+ site_tagline,
+ site_description,
+ admin_user,
+ allowed_users,
+ default_wallet_name,
+ data_folder,
+ disabled_ext,
+ force_https,
+ service_fee,
+ funding_source,
+ ),
+ )
+
+
+async def m001_create_funding_table(db):
+
+ funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
+
+ # Make the funding table, if it does not already exist
+ await db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS funding (
+ id TEXT PRIMARY KEY,
+ backend_wallet TEXT,
+ endpoint TEXT,
+ port INT,
+ read_key TEXT,
+ invoice_key TEXT,
+ admin_key TEXT,
+ cert TEXT,
+ balance INT,
+ selected INT
+ );
+ """
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, selected)
+ VALUES (?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "CLightningWallet",
+ getenv("CLIGHTNING_RPC"),
+ 1 if funding_wallet == "CLightningWallet" else 0,
+ ),
+ )
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "SparkWallet",
+ getenv("SPARK_URL"),
+ getenv("SPARK_TOKEN"),
+ 1 if funding_wallet == "SparkWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LnbitsWallet",
+ getenv("LNBITS_ENDPOINT"),
+ getenv("LNBITS_KEY"),
+ 1 if funding_wallet == "LnbitsWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LndWallet",
+ getenv("LND_GRPC_ENDPOINT"),
+ getenv("LND_GRPC_PORT"),
+ getenv("LND_GRPC_MACAROON"),
+ getenv("LND_GRPC_CERT"),
+ 1 if funding_wallet == "LndWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LndRestWallet",
+ getenv("LND_REST_ENDPOINT"),
+ getenv("LND_REST_MACAROON"),
+ getenv("LND_REST_CERT"),
+ 1 if funding_wallet == "LndWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LNPayWallet",
+ getenv("LNPAY_API_ENDPOINT"),
+ getenv("LNPAY_WALLET_KEY"),
+ getenv("LNPAY_API_KEY"), # this is going in as the cert
+ 1 if funding_wallet == "LNPayWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "LntxbotWallet",
+ getenv("LNTXBOT_API_ENDPOINT"),
+ getenv("LNTXBOT_KEY"),
+ 1 if funding_wallet == "LntxbotWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "OpenNodeWallet",
+ getenv("OPENNODE_API_ENDPOINT"),
+ getenv("OPENNODE_KEY"),
+ 1 if funding_wallet == "OpenNodeWallet" else 0,
+ ),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ VALUES (?, ?, ?, ?, ?)
+ """,
+ (
+ urlsafe_short_hash(),
+ "SparkWallet",
+ getenv("SPARK_URL"),
+ getenv("SPARK_TOKEN"),
+ 1 if funding_wallet == "SparkWallet" else 0,
+ ),
+ )
+
+ ## PLACEHOLDER FOR ECLAIR WALLET
+ # await db.execute(
+ # """
+ # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ # VALUES (?, ?, ?, ?, ?)
+ # """,
+ # (
+ # urlsafe_short_hash(),
+ # "EclairWallet",
+ # getenv("ECLAIR_URL"),
+ # getenv("ECLAIR_PASS"),
+ # 1 if funding_wallet == "EclairWallet" else 0,
+ # ),
+ # )
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
new file mode 100644
index 00000000..c38f17f4
--- /dev/null
+++ b/lnbits/extensions/admin/models.py
@@ -0,0 +1,38 @@
+from typing import NamedTuple
+from sqlite3 import Row
+
+class Admin(NamedTuple):
+ user: str
+ site_title: str
+ site_tagline: str
+ site_description:str
+ allowed_users: str
+ admin_user: str
+ default_wallet_name: str
+ data_folder: str
+ disabled_ext: str
+ force_https: str
+ service_fee: str
+ funding_source: str
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Admin":
+ data = dict(row)
+ return cls(**data)
+
+class Funding(NamedTuple):
+ id: str
+ backend_wallet: str
+ endpoint: str
+ port: str
+ read_key: str
+ invoice_key: str
+ admin_key: str
+ cert: str
+ balance: int
+ selected: int
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Funding":
+ data = dict(row)
+ return cls(**data)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
new file mode 100644
index 00000000..87cf09ef
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -0,0 +1,565 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+Admin
+
+
+
+
+
+
+ Settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Wallet topup
+
+
+
+
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+{% endblock %}
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
new file mode 100644
index 00000000..5e17919c
--- /dev/null
+++ b/lnbits/extensions/admin/views.py
@@ -0,0 +1,20 @@
+from quart import g, render_template, request, jsonify
+import json
+
+from lnbits.decorators import check_user_exists, validate_uuids
+from lnbits.extensions.admin import admin_ext
+from lnbits.core.crud import get_user, create_account
+from .crud import get_admin, get_funding
+from lnbits.settings import WALLET
+
+
+@admin_ext.route("/")
+@validate_uuids(["usr"], required=True)
+@check_user_exists()
+async def index():
+ user_id = g.user
+ admin = await get_admin()
+
+ funding = [{**funding._asdict()} for funding in await get_funding()]
+
+ return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
new file mode 100644
index 00000000..2a61b6f5
--- /dev/null
+++ b/lnbits/extensions/admin/views_api.py
@@ -0,0 +1,41 @@
+from quart import jsonify, g, request
+from http import HTTPStatus
+from .crud import update_wallet_balance
+from lnbits.extensions.admin import admin_ext
+from lnbits.decorators import api_check_wallet_key, api_validate_post_request
+from lnbits.core.crud import get_wallet
+from .crud import get_admin,update_admin
+import json
+
+@admin_ext.route("/api/v1/admin//", methods=["GET"])
+@api_check_wallet_key("admin")
+async def api_update_balance(wallet_id, topup_amount):
+ print(g.data.wallet)
+ try:
+ wallet = await get_wallet(wallet_id)
+ except:
+ return (
+ jsonify({"error": "Not allowed: not an admin"}),
+ HTTPStatus.FORBIDDEN,
+ )
+ print(wallet)
+ print(topup_amount)
+ return jsonify({"status": "Success"}), HTTPStatus.OK
+
+
+@admin_ext.route("/api/v1/admin/", methods=["POST"])
+@api_check_wallet_key("admin")
+@api_validate_post_request(schema={})
+async def api_update_admin():
+ body = await request.get_json()
+ admin = await get_admin()
+ print(g.wallet[2])
+ print(body["admin_user"])
+ if not admin.admin_user == g.wallet[2] and admin.admin_user != None:
+ return (
+ jsonify({"error": "Not allowed: not an admin"}),
+ HTTPStatus.FORBIDDEN,
+ )
+ updated = await update_admin(body)
+ print(updated)
+ return jsonify({"status": "Success"}), HTTPStatus.OK
\ No newline at end of file
From a3b1d9528c92008b42116904f5ebbdf8d9360173 Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Mar 2022 05:11:55 +0000
Subject: [PATCH 0201/1058] old admin setup UI
---
lnbits/core/templates/core/admin.html | 717 ++++++++++++++++++++++++++
1 file changed, 717 insertions(+)
create mode 100644 lnbits/core/templates/core/admin.html
diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html
new file mode 100644
index 00000000..e8176555
--- /dev/null
+++ b/lnbits/core/templates/core/admin.html
@@ -0,0 +1,717 @@
+{% extends "public.html" %} {% from "macros.jinja" import window_vars with
+context %} {% block page %}
+
+
+
+
+
+ Welcome to LNbits
+
+
+ Fill in the information below to setup your LNbits instance. Details
+ can be changed later.
+
+
+
+
+
+
+ Branding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Service settings
+
+
+
+ Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
+ should be filled in for you
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View project in GitHub
+ Donate
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }}
+
+{% endblock %}
From b325566302f079575e478d9ca9eeff47a8f10a1a Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:18:09 +0000
Subject: [PATCH 0202/1058] convert to FastAPI
---
lnbits/extensions/admin/__init__.py | 11 +++-
lnbits/extensions/admin/crud.py | 50 +++++++--------
lnbits/extensions/admin/migrations.py | 23 ++++---
lnbits/extensions/admin/models.py | 51 ++++++++++------
.../admin/templates/admin/index.html | 23 +++++--
lnbits/extensions/admin/views.py | 39 ++++++++----
lnbits/extensions/admin/views_api.py | 61 ++++++++++---------
7 files changed, 151 insertions(+), 107 deletions(-)
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
index d5f26c90..6a56b2bb 100644
--- a/lnbits/extensions/admin/__init__.py
+++ b/lnbits/extensions/admin/__init__.py
@@ -1,10 +1,15 @@
-from quart import Blueprint
+from fastapi import APIRouter
+
from lnbits.db import Database
+from lnbits.helpers import template_renderer
db = Database("ext_admin")
-admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates")
+admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"])
+
+def admin_renderer():
+ return template_renderer(["lnbits/extensions/admin/templates"])
-from .views_api import * # noqa
from .views import * # noqa
+from .views_api import * # noqa
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index cb8f9b5b..872d6c97 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,11 +1,11 @@
from typing import List, Optional
+from lnbits.core.crud import create_payment
+from lnbits.helpers import urlsafe_short_hash
+from lnbits.settings import *
+
from . import db
from .models import Admin, Funding
-from lnbits.settings import *
-from lnbits.helpers import urlsafe_short_hash
-from lnbits.core.crud import create_payment
-from lnbits.db import Connection
def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -22,38 +22,30 @@ def update_wallet_balance(wallet_id: str, amount: int) -> str:
)
return "success"
-
-async def update_admin(
-) -> Optional[Admin]:
- if not CLightningWallet:
- print("poo")
- await db.execute(
- """
- UPDATE admin
- SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ?
- WHERE 1
- """,
- (
-
- ),
- )
- row = await db.fetchone("SELECT * FROM admin WHERE 1")
- return Admin.from_row(row) if row else None
-
-async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]:
+async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ print("UPDATE", q)
await db.execute(
- f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
+ f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
- return Jukebox(**row) if row else None
+ row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,))
+ assert row, "Newly updated settings couldn't be retrieved"
+ return Admin(**row) if row else None
+
+# async def update_admin(user: str, **kwargs) -> Optional[Admin]:
+# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+# await db.execute(
+# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user)
+# )
+# new_settings = await get_admin()
+# return new_settings
async def get_admin() -> List[Admin]:
- row = await db.fetchone("SELECT * FROM admin WHERE 1")
- return Admin.from_row(row) if row else None
+ row = await db.fetchone("SELECT * FROM admin")
+ return Admin(**row) if row else None
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM funding")
- return [Funding.from_row(row) for row in rows]
+ return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 82d934cb..13b76923 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -1,5 +1,7 @@
-from sqlalchemy.exc import OperationalError # type: ignore
from os import getenv
+
+from sqlalchemy.exc import OperationalError # type: ignore
+
from lnbits.helpers import urlsafe_short_hash
@@ -9,7 +11,7 @@ async def m001_create_admin_table(db):
site_tagline = None
site_description = None
allowed_users = None
- admin_user = None
+ admin_users = None
default_wallet_name = None
data_folder = None
disabled_ext = None
@@ -29,8 +31,9 @@ async def m001_create_admin_table(db):
if getenv("LNBITS_ALLOWED_USERS"):
allowed_users = getenv("LNBITS_ALLOWED_USERS")
- if getenv("LNBITS_ADMIN_USER"):
- admin_user = getenv("LNBITS_ADMIN_USER")
+ if getenv("LNBITS_ADMIN_USERS"):
+ admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
+ user = admin_users.split(',')[0]
if getenv("LNBITS_DEFAULT_WALLET_NAME"):
default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
@@ -53,32 +56,32 @@ async def m001_create_admin_table(db):
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
- user TEXT,
+ "user" TEXT,
site_title TEXT,
site_tagline TEXT,
site_description TEXT,
- admin_user TEXT,
+ admin_users TEXT,
allowed_users TEXT,
default_wallet_name TEXT,
data_folder TEXT,
disabled_ext TEXT,
force_https BOOLEAN,
- service_fee INT,
+ service_fee REAL,
funding_source TEXT
);
"""
)
await db.execute(
"""
- INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
+ INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
- user,
+ user.strip(),
site_title,
site_tagline,
site_description,
- admin_user,
+ admin_users[1:],
allowed_users,
default_wallet_name,
data_folder,
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index c38f17f4..4080ff01 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,18 +1,35 @@
-from typing import NamedTuple
from sqlite3 import Row
+from typing import List, Optional
-class Admin(NamedTuple):
+from fastapi import Query
+from pydantic import BaseModel
+
+
+class UpdateAdminSettings(BaseModel):
+ site_title: Optional[str]
+ site_tagline: Optional[str]
+ site_description: Optional[str]
+ allowed_users: Optional[str]
+ admin_users: Optional[str]
+ default_wallet_name: Optional[str]
+ data_folder: Optional[str]
+ disabled_ext: Optional[str]
+ force_https: Optional[bool]
+ service_fee: Optional[float]
+ funding_source: Optional[str]
+
+class Admin(BaseModel):
user: str
- site_title: str
- site_tagline: str
- site_description:str
- allowed_users: str
- admin_user: str
+ site_title: Optional[str]
+ site_tagline: Optional[str]
+ site_description: Optional[str]
+ allowed_users: Optional[str]
+ admin_users: str
default_wallet_name: str
data_folder: str
disabled_ext: str
- force_https: str
- service_fee: str
+ force_https: Optional[bool] = Query(True)
+ service_fee: float
funding_source: str
@classmethod
@@ -20,16 +37,16 @@ class Admin(NamedTuple):
data = dict(row)
return cls(**data)
-class Funding(NamedTuple):
+class Funding(BaseModel):
id: str
backend_wallet: str
- endpoint: str
- port: str
- read_key: str
- invoice_key: str
- admin_key: str
- cert: str
- balance: int
+ endpoint: str = Query(None)
+ port: str = Query(None)
+ read_key: str = Query(None)
+ invoice_key: str = Query(None)
+ admin_key: str = Query(None)
+ cert: str = Query(None)
+ balance: int = Query(None)
selected: int
@classmethod
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 87cf09ef..a6b45625 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -87,7 +87,7 @@
@@ -442,13 +442,14 @@
site_title: '{{admin.site_title}}',
tagline: '{{admin.site_tagline}}',
description: '{{admin.site_description}}',
- admin_user: '{{admin.admin_user}}',
- service_fee: parseInt('{{admin.service_fee}}'),
+ admin_users: '{{admin.admin_users}}',
+ service_fee: parseFloat('{{admin.service_fee}}'),
default_wallet_name: '{{admin.default_wallet_name}}',
data_folder: '{{admin.data_folder}}',
funding_source_primary: '{{admin.funding_source}}',
disabled_ext: '{{admin.disabled_ext}}'.split(','),
edited: [],
+ funding: {},
senddata: {}
}
},
@@ -528,15 +529,27 @@
},
UpdateLNbits: function () {
var self = this
- console.log(self.data.admin)
+ let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin
+ let data = {
+ site_title,
+ site_tagline: this.data.admin.tagline,
+ site_description: this.data.admin.description,
+ admin_users: admin_users.toString(),
+ default_wallet_name,
+ data_folder,
+ disabled_ext: disabled_ext.toString(),
+ service_fee,
+ funding_source: funding_source_primary}
+ console.log(data)
LNbits.api
.request(
'POST',
'/admin/api/v1/admin/',
self.g.user.wallets[0].adminkey,
- self.data.admin
+ data
)
.then(function (response) {
+ console.log(response.data)
self.$q.notify({
type: 'positive',
message:
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 5e17919c..00a0c99f 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -1,20 +1,33 @@
-from quart import g, render_template, request, jsonify
-import json
+from email.policy import default
+from os import getenv
-from lnbits.decorators import check_user_exists, validate_uuids
+from fastapi import Request
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.responses import HTMLResponse
+
+from lnbits.core.models import User
+from lnbits.decorators import check_user_exists
from lnbits.extensions.admin import admin_ext
-from lnbits.core.crud import get_user, create_account
+from lnbits.requestvars import g
+
+from . import admin_ext, admin_renderer
from .crud import get_admin, get_funding
-from lnbits.settings import WALLET
+templates = Jinja2Templates(directory="templates")
-@admin_ext.route("/")
-@validate_uuids(["usr"], required=True)
-@check_user_exists()
-async def index():
- user_id = g.user
+@admin_ext.get("/", response_class=HTMLResponse)
+async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
+ print(g())
+ funding = [f.dict() for f in await get_funding()]
- funding = [{**funding._asdict()} for funding in await get_funding()]
-
- return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding)
+ print("ADMIN", admin.dict())
+ return admin_renderer().TemplateResponse(
+ "admin/index.html", {
+ "request": request,
+ "user": user.dict(),
+ "admin": admin.dict(),
+ "funding": funding
+ }
+ )
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 2a61b6f5..b2c65be2 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,41 +1,42 @@
-from quart import jsonify, g, request
from http import HTTPStatus
-from .crud import update_wallet_balance
-from lnbits.extensions.admin import admin_ext
-from lnbits.decorators import api_check_wallet_key, api_validate_post_request
-from lnbits.core.crud import get_wallet
-from .crud import get_admin,update_admin
-import json
-@admin_ext.route("/api/v1/admin//", methods=["GET"])
-@api_check_wallet_key("admin")
-async def api_update_balance(wallet_id, topup_amount):
- print(g.data.wallet)
+from fastapi import Body, Depends, Request
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_wallet
+from lnbits.decorators import WalletTypeInfo, require_admin_key
+from lnbits.extensions.admin import admin_ext
+from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+
+from .crud import get_admin, update_admin, update_wallet_balance
+
+
+@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
+async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)):
+ print(g.wallet)
try:
wallet = await get_wallet(wallet_id)
except:
- return (
- jsonify({"error": "Not allowed: not an admin"}),
- HTTPStatus.FORBIDDEN,
- )
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
print(wallet)
print(topup_amount)
- return jsonify({"status": "Success"}), HTTPStatus.OK
+ return {"status": "Success"}
-@admin_ext.route("/api/v1/admin/", methods=["POST"])
-@api_check_wallet_key("admin")
-@api_validate_post_request(schema={})
-async def api_update_admin():
- body = await request.get_json()
+@admin_ext.post("/api/v1/admin/", status_code=HTTPStatus.OK)
+async def api_update_admin(
+ request: Request,
+ data: UpdateAdminSettings = Body(...),
+ g: WalletTypeInfo = Depends(require_admin_key)
+ ):
admin = await get_admin()
- print(g.wallet[2])
- print(body["admin_user"])
- if not admin.admin_user == g.wallet[2] and admin.admin_user != None:
- return (
- jsonify({"error": "Not allowed: not an admin"}),
- HTTPStatus.FORBIDDEN,
- )
- updated = await update_admin(body)
+ print(data)
+ if not admin.user == g.wallet.user:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
+ updated = await update_admin(user=g.wallet.user, **data.dict())
print(updated)
- return jsonify({"status": "Success"}), HTTPStatus.OK
\ No newline at end of file
+ return {"status": "Success"}
From b4885de9e2fcf598994dd5ca2360cc29629aaa23 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:18:58 +0000
Subject: [PATCH 0203/1058] remove core admin html (renamed for now)
---
lnbits/core/templates/core/core_admin.html | 717 +++++++++++++++++++++
1 file changed, 717 insertions(+)
create mode 100644 lnbits/core/templates/core/core_admin.html
diff --git a/lnbits/core/templates/core/core_admin.html b/lnbits/core/templates/core/core_admin.html
new file mode 100644
index 00000000..835fc00a
--- /dev/null
+++ b/lnbits/core/templates/core/core_admin.html
@@ -0,0 +1,717 @@
+{% extends "public.html" %} {% from "macros.jinja" import window_vars with
+context %} {% block page %}
+
+
+
+
+
+ Welcome to LNbits
+
+
+ Fill in the information below to setup your LNbits instance. Details
+ can be changed later.
+
+
+
+
+
+
+ Branding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Service settings
+
+
+
+ Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
+ should be filled in for you
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View project in GitHub
+ Donate
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }}
+
+{% endblock %}
From c41b4e714c2e20c36d6a262c77966598b1fd85c4 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:21:38 +0000
Subject: [PATCH 0204/1058] typo
---
.env.example | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.env.example b/.env.example
index bfaeb515..4192f82e 100644
--- a/.env.example
+++ b/.env.example
@@ -5,7 +5,7 @@ DEBUG=false
LNBITS_ADMIN_USERS="" # User IDs seperated by comma
LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS
LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
From 922206b365cff523a12bf5a8014b544aa88398f3 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:22:23 +0000
Subject: [PATCH 0205/1058] add admin_ui env
---
lnbits/settings.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/settings.py b/lnbits/settings.py
index 3f4e31cc..43cb87cb 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,5 +1,6 @@
import importlib
import subprocess
+from email.policy import default
from os import path
from typing import List
@@ -27,6 +28,7 @@ LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None)
LNBITS_ALLOWED_USERS: List[str] = [
x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str)
]
+LNBITS_ADMIN_UI = env.bool("LNBITS_ADMIN_UI", default=False)
LNBITS_ADMIN_USERS: List[str] = [
x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str)
]
From b47ed3068105f2526365f6708efe5134a5fe1c0e Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:23:16 +0000
Subject: [PATCH 0206/1058] add db config at startup
---
lnbits/commands.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 0f7454f2..8c39c338 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -52,6 +52,25 @@ def bundle_vendored():
with open(outputpath, "w") as f:
f.write(output)
+async def get_admin_settings():
+ from lnbits.extensions.admin.models import Admin
+
+ async with core_db.connect() as conn:
+
+ if conn.type == SQLITE:
+ exists = await conn.fetchone(
+ "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
+ )
+ elif conn.type in {POSTGRES, COCKROACH}:
+ exists = await conn.fetchone(
+ "SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
+ )
+ if not exists:
+ return False
+
+ row = await conn.fetchone("SELECT * from admin")
+
+ return Admin(**row) if row else None
async def migrate_databases():
"""Creates the necessary databases if they don't exist already; or migrates them."""
From 4bf17c5df2d07443c972be9356be710e4578c79b Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:23:53 +0000
Subject: [PATCH 0207/1058] get admin settings at startup
---
lnbits/app.py | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 075828ef..7ff9e4eb 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -18,6 +18,7 @@ from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
+from .commands import get_admin_settings
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -42,6 +43,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""Create application factory.
:param config_object: The configuration object to use.
"""
+<<<<<<< HEAD
configure_logger()
app = FastAPI(
@@ -53,6 +55,14 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
+=======
+ app = FastAPI()
+
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ check_settings(app)
+
+ app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
+>>>>>>> e3a1b3ae (get admin settings at startup)
app.mount(
"/core/static",
StaticFiles(packages=[("lnbits.core", "static")]),
@@ -64,7 +74,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
app.add_middleware(
CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"]
)
-
g().config = lnbits.settings
g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}"
@@ -101,6 +110,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
+def check_settings(app: FastAPI):
+ @app.on_event("startup")
+ async def check_settings_admin():
+ while True:
+ admin_set = await get_admin_settings()
+ if admin_set :
+ break
+ print("ERROR:", admin_set)
+ await asyncio.sleep(5)
+ # admin_set = await get_admin_settings()
+ g().admin_conf = admin_set
+
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
async def check_wallet_status():
From 48d6a89e5ba9e1e082a2ff189b3307d685f521e4 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Sat, 12 Mar 2022 14:24:11 +0000
Subject: [PATCH 0208/1058] remove core admin.html
---
lnbits/core/templates/core/admin.html | 717 --------------------------
1 file changed, 717 deletions(-)
delete mode 100644 lnbits/core/templates/core/admin.html
diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html
deleted file mode 100644
index e8176555..00000000
--- a/lnbits/core/templates/core/admin.html
+++ /dev/null
@@ -1,717 +0,0 @@
-{% extends "public.html" %} {% from "macros.jinja" import window_vars with
-context %} {% block page %}
-
-
-
-
-
- Welcome to LNbits
-
-
- Fill in the information below to setup your LNbits instance. Details
- can be changed later.
-
-
-
-
-
-
- Branding
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Service settings
-
-
-
- Funding source information (at least one required) *if installed through RaspiBlitz, MyNode, etc, details
- should be filled in for you
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- View project in GitHub
- Donate
-
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(funding) }}
-
-{% endblock %}
From 87bee88de403723b9d98f64716c573e21289a50a Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 18 Mar 2022 16:55:31 +0000
Subject: [PATCH 0209/1058] refactor ui
---
.../admin/templates/admin/index.html | 727 +++++++++++++++++-
1 file changed, 712 insertions(+), 15 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index a6b45625..65ac9f33 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1,6 +1,670 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
+
+
+
+
+
+
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+ tab = val.name"
+ >
+
+
+
+
+
+
+
+ Wallets Management
+
+
+
+
+
Funding Source Info
+
+ {%raw%}
+ Funding Source: {{data.admin.funding_source}}
+ Balance: {{data.admin.balance / 1000}} sats
+ {%endraw%}
+
+
+
+
+
+
+
+
+ TopUp a wallet
+
+
+
+
+
+
+
+
+
Funding Sources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+ User Management
+
+
+ Super Admin: {% raw
+ %}{{this.data.admin.user}}{% endraw %}
+
+
+
+
Admin Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
Allowed Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
+
+
+ Server Management
+
+
+
+
+
Server Info
+
+ {%raw%}
+ SQlite: {{data.admin.data_folder}}
+ Postgres: {{data.admin.database_url}}
+ {%endraw%}
+
+
+
+
+
+
+
+
Miscelaneous
+
+
+ Force HTTPS
+ Prefer secure URLs
+
+
+
+
+
+
+
+ Hide API
+ Hides wallet api, extensions can choose to honor
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+ UI Management
+
+
+
+
+
+
+
+
Default Wallet Name
+
+
+
+
+
+
+
+
+
Advertisement Slots
+
+
+
+
+ {% raw %}
+
+ {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
+
+ {% endraw %}
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+
+
+
+
Admin
-
+
@@ -426,6 +1090,7 @@
return {
wallet: {data: {}},
cancel: {},
+ tab: 'funding',
data: {
funding_source: [
'CLightningWallet',
@@ -436,24 +1101,14 @@
'LnbitsWallet',
'OpenNodeWallet'
],
-
+
admin: {
- user: '{{ user.id }}',
- site_title: '{{admin.site_title}}',
- tagline: '{{admin.site_tagline}}',
- description: '{{admin.site_description}}',
- admin_users: '{{admin.admin_users}}',
- service_fee: parseFloat('{{admin.service_fee}}'),
- default_wallet_name: '{{admin.default_wallet_name}}',
- data_folder: '{{admin.data_folder}}',
- funding_source_primary: '{{admin.funding_source}}',
- disabled_ext: '{{admin.disabled_ext}}'.split(','),
edited: [],
funding: {},
senddata: {}
}
},
-
+ themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'],
options: [
'bleskomat',
'captcha',
@@ -489,9 +1144,51 @@
for (i = 0; i < funding.length; i++) {
self.data.admin.funding[funding[i].backend_wallet] = funding[i]
}
- console.log(self.data.admin)
+ let settings = JSON.parse('{{ settings | tojson|safe }}')
+ settings.balance = '{{ balance }}'
+ this.data.admin = {...this.data.admin, ...settings}
+ console.log(this.g.user)
},
methods: {
+ addAdminUser(){
+ let addUser = this.data.admin_users_add
+ let admin_users = this.data.admin.admin_users
+ if(addUser.length && !admin_users.includes(addUser)){
+ admin_users.push(addUser)
+ this.data.admin.admin_users = admin_users
+ this.data.admin_users_add = ""
+ }
+ },
+ removeAdminUser(user){
+ let admin_users = this.data.admin.admin_users
+ this.data.admin.admin_users = admin_users.filter(u => u !== user)
+ },
+ addAllowedUser(){
+ let addUser = this.data.allowed_users_add
+ let allowed_users = this.data.admin.allowed_users
+ if(addUser.length && !allowed_users.includes(addUser)){
+ allowed_users.push(addUser)
+ this.data.admin.allowed_users = allowed_users
+ this.data.allowed_users_add = ""
+ }
+ },
+ removeAllowedUser(user){
+ let allowed_users = this.data.admin.allowed_users
+ this.data.admin.allowed_users = allowed_users.filter(u => u !== user)
+ },
+ addAdSpace(){
+ let adSpace = this.data.ad_space_add
+ let spaces = this.data.admin.ad_space
+ if(adSpace.length && !spaces.includes(adSpace)){
+ spaces.push(adSpace)
+ this.data.admin.ad_space = spaces
+ this.data.ad_space_add = ""
+ }
+ },
+ removeAdSpace(ad){
+ let spaces = this.data.admin.ad_space
+ this.data.admin.ad_space = spaces.filter(s => s !== ad)
+ },
topupWallet: function () {
var self = this
LNbits.api
From 8c1c7d13b87ba7b7408dccd719a9a9c66e6e4325 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 18 Mar 2022 16:59:06 +0000
Subject: [PATCH 0210/1058] make it work from g()
---
lnbits/app.py | 34 +++---
lnbits/config.py | 62 ++++++++++
lnbits/extensions/admin/crud.py | 2 +-
lnbits/extensions/admin/migrations.py | 162 +++++++++++++++++---------
lnbits/extensions/admin/models.py | 27 +++--
lnbits/extensions/admin/views.py | 8 +-
lnbits/settings.py | 2 +-
7 files changed, 218 insertions(+), 79 deletions(-)
create mode 100644 lnbits/config.py
diff --git a/lnbits/app.py b/lnbits/app.py
index 7ff9e4eb..1ffedb54 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -19,6 +19,7 @@ import lnbits.settings
from lnbits.core.tasks import register_task_listeners
from .commands import get_admin_settings
+from .config import WALLET, conf
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -29,7 +30,8 @@ from .helpers import (
url_for_vendored,
)
from .requestvars import g
-from .settings import WALLET
+
+# from .settings import WALLET
from .tasks import (
catch_everything_and_restart,
check_pending_payments,
@@ -43,7 +45,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""Create application factory.
:param config_object: The configuration object to use.
"""
-<<<<<<< HEAD
configure_logger()
app = FastAPI(
@@ -55,20 +56,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
-=======
- app = FastAPI()
-
- if lnbits.settings.LNBITS_ADMIN_UI:
- check_settings(app)
-
- app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
->>>>>>> e3a1b3ae (get admin settings at startup)
app.mount(
"/core/static",
StaticFiles(packages=[("lnbits.core", "static")]),
name="core_static",
)
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ g().admin_conf = conf
+ check_settings(app)
+
+ g().WALLET = WALLET
+
origins = ["*"]
app.add_middleware(
@@ -109,18 +108,27 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
-
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
+
+ def removeEmptyString(arr):
+ return list(filter(None, arr))
+
while True:
admin_set = await get_admin_settings()
if admin_set :
break
print("ERROR:", admin_set)
await asyncio.sleep(5)
- # admin_set = await get_admin_settings()
- g().admin_conf = admin_set
+
+ admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
+ admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(','))
+ admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(','))
+ admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(','))
+ admin_set.theme = removeEmptyString(admin_set.theme.split(','))
+ admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
+ g().admin_conf = conf.copy(update=admin_set.dict())
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
diff --git a/lnbits/config.py b/lnbits/config.py
new file mode 100644
index 00000000..02e8cf53
--- /dev/null
+++ b/lnbits/config.py
@@ -0,0 +1,62 @@
+import importlib
+import json
+from os import getenv, path
+from typing import List, Optional
+
+from pydantic import BaseSettings, Field, validator
+
+wallets_module = importlib.import_module("lnbits.wallets")
+wallet_class = getattr(
+ wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet")
+)
+
+WALLET = wallet_class()
+
+def list_parse_fallback(v):
+ try:
+ return json.loads(v)
+ except Exception as e:
+ return v.replace(' ','').split(',')
+
+class Settings(BaseSettings):
+ # users
+ admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
+ allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
+ admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS")
+ disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS")
+ funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS")
+ # ops
+ data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER")
+ database_url: str = Field(default=None, env="LNBITS_DATABASE_URL")
+ force_https: bool = Field(default=True, env="LNBITS_FORCE_HTTPS")
+ service_fee: float = Field(default=0, env="LNBITS_SERVICE_FEE")
+ hide_api: bool = Field(default=False, env="LNBITS_HIDE_API")
+ denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
+ # Change theme
+ site_title: str = Field(default=None, env="LNBITS_SITE_TITLE")
+ site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE")
+ site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
+ default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME")
+ theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
+ ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
+ # .env
+ env: Optional[str]
+ debug: Optional[str]
+ host: Optional[str]
+ port: Optional[str]
+ lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
+
+ # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True)
+ # def validate(cls, val):
+ # print(val)
+ # return val.split(',')
+
+ class Config:
+ env_file = ".env"
+ env_file_encoding = "utf-8"
+ case_sensitive = False
+ json_loads = list_parse_fallback
+
+
+conf = Settings()
+WALLET = wallet_class()
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 872d6c97..6fccb8ee 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -40,7 +40,7 @@ async def update_admin(user: str, **kwargs) -> Admin:
# new_settings = await get_admin()
# return new_settings
-async def get_admin() -> List[Admin]:
+async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 13b76923..574f772d 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -2,93 +2,151 @@ from os import getenv
from sqlalchemy.exc import OperationalError # type: ignore
+from lnbits.config import conf
from lnbits.helpers import urlsafe_short_hash
async def m001_create_admin_table(db):
- user = None
- site_title = None
- site_tagline = None
- site_description = None
- allowed_users = None
- admin_users = None
- default_wallet_name = None
- data_folder = None
- disabled_ext = None
- force_https = True
- service_fee = 0
- funding_source = ""
+ # users/server
+ user = conf.admin_users[0]
+ admin_users = ",".join(conf.admin_users)
+ allowed_users = ",".join(conf.allowed_users)
+ admin_ext = ",".join(conf.admin_ext)
+ disabled_ext = ",".join(conf.disabled_ext)
+ funding_source = conf.funding_source
+ #operational
+ data_folder = conf.data_folder
+ database_url = conf.database_url
+ force_https = conf.force_https
+ service_fee = conf.service_fee
+ hide_api = conf.hide_api
+ denomination = conf.denomination
+ # Theme'ing
+ site_title = conf.site_title
+ site_tagline = conf.site_tagline
+ site_description = conf.site_description
+ default_wallet_name = conf.default_wallet_name
+ theme = ",".join(conf.theme)
+ ad_space = ",".join(conf.ad_space)
- if getenv("LNBITS_SITE_TITLE"):
- site_title = getenv("LNBITS_SITE_TITLE")
+ # if getenv("LNBITS_ADMIN_EXTENSIONS"):
+ # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS")
- if getenv("LNBITS_SITE_TAGLINE"):
- site_tagline = getenv("LNBITS_SITE_TAGLINE")
+ # if getenv("LNBITS_DATABASE_URL"):
+ # database_url = getenv("LNBITS_DATABASE_URL")
- if getenv("LNBITS_SITE_DESCRIPTION"):
- site_description = getenv("LNBITS_SITE_DESCRIPTION")
+ # if getenv("LNBITS_HIDE_API"):
+ # hide_api = getenv("LNBITS_HIDE_API")
- if getenv("LNBITS_ALLOWED_USERS"):
- allowed_users = getenv("LNBITS_ALLOWED_USERS")
+ # if getenv("LNBITS_THEME_OPTIONS"):
+ # theme = getenv("LNBITS_THEME_OPTIONS")
- if getenv("LNBITS_ADMIN_USERS"):
- admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
- user = admin_users.split(',')[0]
+ # if getenv("LNBITS_AD_SPACE"):
+ # ad_space = getenv("LNBITS_AD_SPACE")
- if getenv("LNBITS_DEFAULT_WALLET_NAME"):
- default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
+ # if getenv("LNBITS_SITE_TITLE"):
+ # site_title = getenv("LNBITS_SITE_TITLE")
- if getenv("LNBITS_DATA_FOLDER"):
- data_folder = getenv("LNBITS_DATA_FOLDER")
+ # if getenv("LNBITS_SITE_TAGLINE"):
+ # site_tagline = getenv("LNBITS_SITE_TAGLINE")
- if getenv("LNBITS_DISABLED_EXTENSIONS"):
- disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+ # if getenv("LNBITS_SITE_DESCRIPTION"):
+ # site_description = getenv("LNBITS_SITE_DESCRIPTION")
- if getenv("LNBITS_FORCE_HTTPS"):
- force_https = getenv("LNBITS_FORCE_HTTPS")
+ # if getenv("LNBITS_ALLOWED_USERS"):
+ # allowed_users = getenv("LNBITS_ALLOWED_USERS")
- if getenv("LNBITS_SERVICE_FEE"):
- service_fee = getenv("LNBITS_SERVICE_FEE")
+ # if getenv("LNBITS_ADMIN_USERS"):
+ # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
+ # user = admin_users.split(',')[0]
+
+ # if getenv("LNBITS_DEFAULT_WALLET_NAME"):
+ # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
- if getenv("LNBITS_BACKEND_WALLET_CLASS"):
- funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
+ # if getenv("LNBITS_DATA_FOLDER"):
+ # data_folder = getenv("LNBITS_DATA_FOLDER")
+
+ # if getenv("LNBITS_DISABLED_EXTENSIONS"):
+ # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
+
+ # if getenv("LNBITS_FORCE_HTTPS"):
+ # force_https = getenv("LNBITS_FORCE_HTTPS")
+
+ # if getenv("LNBITS_SERVICE_FEE"):
+ # service_fee = getenv("LNBITS_SERVICE_FEE")
+
+ # if getenv("LNBITS_DENOMINATION"):
+ # denomination = getenv("LNBITS_DENOMINATION", "sats")
+
+ # if getenv("LNBITS_BACKEND_WALLET_CLASS"):
+ # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
- "user" TEXT,
+ "user" TEXT PRIMARY KEY,
+ admin_users TEXT,
+ allowed_users TEXT,
+ admin_ext TEXT,
+ disabled_ext TEXT,
+ funding_source TEXT,
+ data_folder TEXT,
+ database_url TEXT,
+ force_https BOOLEAN,
+ service_fee REAL,
+ hide_api BOOLEAN,
+ denomination TEXT,
site_title TEXT,
site_tagline TEXT,
site_description TEXT,
- admin_users TEXT,
- allowed_users TEXT,
default_wallet_name TEXT,
- data_folder TEXT,
- disabled_ext TEXT,
- force_https BOOLEAN,
- service_fee REAL,
- funding_source TEXT
+ theme TEXT,
+ ad_space TEXT
);
"""
)
await db.execute(
"""
- INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- user.strip(),
+ INSERT INTO admin (
+ "user",
+ admin_users,
+ allowed_users,
+ admin_ext,
+ disabled_ext,
+ funding_source,
+ data_folder,
+ database_url,
+ force_https,
+ service_fee,
+ hide_api,
+ denomination,
site_title,
site_tagline,
site_description,
- admin_users[1:],
- allowed_users,
default_wallet_name,
- data_folder,
+ theme,
+ ad_space)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ user,
+ admin_users,
+ allowed_users,
+ admin_ext,
disabled_ext,
+ funding_source,
+ data_folder,
+ database_url,
force_https,
service_fee,
- funding_source,
+ hide_api,
+ denomination,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ theme,
+ ad_space,
),
)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 4080ff01..f7c64de5 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -2,7 +2,7 @@ from sqlite3 import Row
from typing import List, Optional
from fastapi import Query
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
class UpdateAdminSettings(BaseModel):
@@ -19,18 +19,27 @@ class UpdateAdminSettings(BaseModel):
funding_source: Optional[str]
class Admin(BaseModel):
+ # users
user: str
+ admin_users: Optional[str]
+ allowed_users: Optional[str]
+ admin_ext: Optional[str]
+ disabled_ext: Optional[str]
+ funding_source: Optional[str]
+ # ops
+ data_folder: Optional[str]
+ database_url: Optional[str]
+ force_https: bool = Field(default=True)
+ service_fee: float = Field(default=0)
+ hide_api: bool = Field(default=False)
+ # Change theme
site_title: Optional[str]
site_tagline: Optional[str]
site_description: Optional[str]
- allowed_users: Optional[str]
- admin_users: str
- default_wallet_name: str
- data_folder: str
- disabled_ext: str
- force_https: Optional[bool] = Query(True)
- service_fee: float
- funding_source: str
+ default_wallet_name: Optional[str]
+ denomination: str = Field(default="sats")
+ theme: Optional[str]
+ ad_space: Optional[str]
@classmethod
def from_row(cls, row: Row) -> "Admin":
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 00a0c99f..105f05a1 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -19,15 +19,17 @@ templates = Jinja2Templates(directory="templates")
@admin_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
- print(g())
funding = [f.dict() for f in await get_funding()]
-
+ error, balance = await g().WALLET.status()
print("ADMIN", admin.dict())
+ print(g().admin_conf)
return admin_renderer().TemplateResponse(
"admin/index.html", {
"request": request,
"user": user.dict(),
"admin": admin.dict(),
- "funding": funding
+ "funding": funding,
+ "settings": g().admin_conf.dict(),
+ "balance": balance
}
)
diff --git a/lnbits/settings.py b/lnbits/settings.py
index 43cb87cb..ed5c77f7 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -4,7 +4,7 @@ from email.policy import default
from os import path
from typing import List
-from environs import Env # type: ignore
+from environs import Env
env = Env()
env.read_env()
From a72ed98997656a709b00d171c27c387c26dfe0bd Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:28:07 +0000
Subject: [PATCH 0211/1058] topup wallet endpoint
---
lnbits/extensions/admin/crud.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 6fccb8ee..683558f9 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,26 +1,31 @@
+import json
from typing import List, Optional
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
from lnbits.settings import *
+from lnbits.tasks import internal_invoice_queue
from . import db
from .models import Admin, Funding
-def update_wallet_balance(wallet_id: str, amount: int) -> str:
+async def update_wallet_balance(wallet_id: str, amount: int) -> str:
temp_id = f"temp_{urlsafe_short_hash()}"
internal_id = f"internal_{urlsafe_short_hash()}"
- create_payment(
+
+ payment = await create_payment(
wallet_id=wallet_id,
checking_id=internal_id,
payment_request="admin_internal",
payment_hash="admin_internal",
- amount=amount * 1000,
+ amount=amount*1000,
memo="Admin top up",
pending=False,
)
- return "success"
+ # manually send this for now
+ await internal_invoice_queue.put(internal_id)
+ return payment
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
From 42a9af986ab3620bdb61270961c6adbda4a66c95 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:28:44 +0000
Subject: [PATCH 0212/1058] update admin settings in db
---
lnbits/extensions/admin/models.py | 29 +++++++++++++++++-----------
lnbits/extensions/admin/views_api.py | 8 ++++----
2 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index f7c64de5..36d9b815 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -6,17 +6,24 @@ from pydantic import BaseModel, Field
class UpdateAdminSettings(BaseModel):
- site_title: Optional[str]
- site_tagline: Optional[str]
- site_description: Optional[str]
- allowed_users: Optional[str]
- admin_users: Optional[str]
- default_wallet_name: Optional[str]
- data_folder: Optional[str]
- disabled_ext: Optional[str]
- force_https: Optional[bool]
- service_fee: Optional[float]
- funding_source: Optional[str]
+ # users
+ admin_users: str = Query(None)
+ allowed_users: str = Query(None)
+ admin_ext: str = Query(None)
+ disabled_ext: str = Query(None)
+ funding_source: str = Query(None)
+ # ops
+ force_https: bool = Query(None)
+ service_fee: float = Query(None, ge=0)
+ hide_api: bool = Query(None)
+ # Change theme
+ site_title: str = Query(None)
+ site_tagline: str = Query(None)
+ site_description: str = Query(None)
+ default_wallet_name: str = Query(None)
+ denomination: str = Query(None)
+ theme: str = Query(None)
+ ad_space: str = Query(None)
class Admin(BaseModel):
# users
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index b2c65be2..cb526aa5 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -12,16 +12,16 @@ from .crud import get_admin, update_admin, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
-async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)):
- print(g.wallet)
+async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)):
try:
wallet = await get_wallet(wallet_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
- print(wallet)
- print(topup_amount)
+
+ await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
+
return {"status": "Success"}
From 2ebb4448d5ff8daa3b1c4729ce33dd4c5c744c7b Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 10:29:18 +0000
Subject: [PATCH 0213/1058] update settings and topup logic
---
.../admin/templates/admin/index.html | 91 ++++++++++++-------
1 file changed, 57 insertions(+), 34 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 65ac9f33..e9ddc7c4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -30,7 +30,7 @@
-
+
@@ -61,7 +61,7 @@
@@ -577,7 +577,6 @@
Site Description
s !== ad)
},
- topupWallet: function () {
- var self = this
+ topupWallet() {
LNbits.api
.request(
'GET',
'/admin/api/v1/admin/' +
- self.wallet.id +
+ this.wallet.data.id +
'/' +
- self.wallet.data.amount,
- self.g.user.wallets[0].adminkey
+ this.wallet.data.amount,
+ this.g.user.wallets[0].adminkey
)
- .then(function (response) {
- self.$q.notify({
+ .then((response) => {
+ this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- self.wallet.amount +
- ' to ' +
- self.wallet.id,
+ 'Success! Added ' +
+ this.wallet.data.amount +
+ ' to ' +
+ this.wallet.data.id,
icon: null
})
+ this.wallet.data = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -1224,36 +1224,59 @@
self.data.admin.edited.push(source)
console.log(self.data.admin.edited)
},
- UpdateLNbits: function () {
- var self = this
- let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin
+ UpdateLNbits() {
+ let {
+ admin_users,
+ allowed_users,
+ admin_ext,
+ disabled_ext,
+ funding_source,
+ force_https,
+ service_fee,
+ hide_api,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ denomination,
+ theme,
+ ad_space
+ } = this.data.admin
+ //console.log("this", this.data.admin)
let data = {
- site_title,
- site_tagline: this.data.admin.tagline,
- site_description: this.data.admin.description,
- admin_users: admin_users.toString(),
- default_wallet_name,
- data_folder,
+ admin_users: admin_users.toString(),
+ allowed_users: allowed_users.toString(),
+ admin_ext: admin_ext.toString(),
disabled_ext: disabled_ext.toString(),
- service_fee,
- funding_source: funding_source_primary}
+ funding_source,
+ force_https,
+ service_fee,
+ hide_api,
+ site_title,
+ site_tagline,
+ site_description,
+ default_wallet_name,
+ denomination,
+ theme: theme.toString(),
+ ad_space: ad_space.toString()
+ }
console.log(data)
LNbits.api
.request(
'POST',
'/admin/api/v1/admin/',
- self.g.user.wallets[0].adminkey,
+ this.g.user.wallets[0].adminkey,
data
)
- .then(function (response) {
+ .then(response => {
console.log(response.data)
- self.$q.notify({
+ this.$q.notify({
type: 'positive',
message:
'Success! Added ' +
- self.wallet.amount +
+ this.wallet.amount +
' to ' +
- self.wallet.id,
+ this.wallet.id,
icon: null
})
})
From 3082a393436b88a24d860f87983560e758a02f66 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:34:47 +0000
Subject: [PATCH 0214/1058] make removeEmptyString fn as helper fn
---
lnbits/app.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 1ffedb54..c8f5c60a 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -26,6 +26,7 @@ from .helpers import (
get_css_vendored,
get_js_vendored,
get_valid_extensions,
+ removeEmptyString,
template_renderer,
url_for_vendored,
)
@@ -112,9 +113,6 @@ def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
- def removeEmptyString(arr):
- return list(filter(None, arr))
-
while True:
admin_set = await get_admin_settings()
if admin_set :
From c419bd27ebcd212da37f27994e630df08bdba7d4 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:35:15 +0000
Subject: [PATCH 0215/1058] add some defaults
---
lnbits/config.py | 6 +++---
lnbits/extensions/admin/models.py | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 02e8cf53..b2fbfff1 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -33,10 +33,10 @@ class Settings(BaseSettings):
hide_api: bool = Field(default=False, env="LNBITS_HIDE_API")
denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
# Change theme
- site_title: str = Field(default=None, env="LNBITS_SITE_TITLE")
- site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE")
+ site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE")
+ site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
- default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME")
+ default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 36d9b815..0f25679d 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -17,11 +17,11 @@ class UpdateAdminSettings(BaseModel):
service_fee: float = Query(None, ge=0)
hide_api: bool = Query(None)
# Change theme
- site_title: str = Query(None)
- site_tagline: str = Query(None)
+ site_title: str = Query("LNbits")
+ site_tagline: str = Query("free and open-source lightning wallet")
site_description: str = Query(None)
- default_wallet_name: str = Query(None)
- denomination: str = Query(None)
+ default_wallet_name: str = Query("LNbits wallet")
+ denomination: str = Query("sats")
theme: str = Query(None)
ad_space: str = Query(None)
From 0897d0476356d1956711fc0f1c6448fbb88275fb Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:36:04 +0000
Subject: [PATCH 0216/1058] removeEmtpy sting as helper fn
---
lnbits/helpers.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e213240c..e456f715 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -154,8 +154,20 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s
url = f"{base}{endpoint}{url_params}"
return url
+def removeEmptyString(arr):
+ return list(filter(None, arr))
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
+ if(settings.LNBITS_ADMIN_UI):
+ _ = g().admin_conf
+ settings.LNBITS_AD_SPACE = _.ad_space
+ settings.LNBITS_HIDE_API = _.hide_api
+ settings.LNBITS_SITE_TITLE = _.site_title
+ settings.LNBITS_DENOMINATION = _.denomination
+ settings.LNBITS_SITE_TAGLINE = _.site_tagline
+ settings.LNBITS_SITE_DESCRIPTION = _.site_description
+ settings.LNBITS_THEME_OPTIONS = _.theme
+
t = Jinja2Templates(
loader=jinja2.FileSystemLoader(
["lnbits/templates", "lnbits/core/templates", *additional_folders]
From 8c93aa304f0d484e908cd40c0a851b95fbbfd992 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:41:11 +0000
Subject: [PATCH 0217/1058] cleanup
---
lnbits/extensions/admin/crud.py | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 683558f9..e14ad194 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -1,9 +1,7 @@
-import json
-from typing import List, Optional
+from typing import List
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
-from lnbits.settings import *
from lnbits.tasks import internal_invoice_queue
from . import db
@@ -37,14 +35,6 @@ async def update_admin(user: str, **kwargs) -> Admin:
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
-# async def update_admin(user: str, **kwargs) -> Optional[Admin]:
-# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
-# await db.execute(
-# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user)
-# )
-# new_settings = await get_admin()
-# return new_settings
-
async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
From 5ce73697d47aa6eb96bb6d69c7da1e4521bc4943 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:42:28 +0000
Subject: [PATCH 0218/1058] make string to list
---
lnbits/extensions/admin/views_api.py | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index cb526aa5..1d4e6a9c 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,5 +1,6 @@
from http import HTTPStatus
+# from config import conf
from fastapi import Body, Depends, Request
from starlette.exceptions import HTTPException
@@ -7,6 +8,8 @@ from lnbits.core.crud import get_wallet
from lnbits.decorators import WalletTypeInfo, require_admin_key
from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+from lnbits.helpers import removeEmptyString
+from lnbits.requestvars import g
from .crud import get_admin, update_admin, update_wallet_balance
@@ -19,7 +22,7 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
-
+
await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
return {"status": "Success"}
@@ -29,14 +32,24 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
async def api_update_admin(
request: Request,
data: UpdateAdminSettings = Body(...),
- g: WalletTypeInfo = Depends(require_admin_key)
+ w: WalletTypeInfo = Depends(require_admin_key)
):
admin = await get_admin()
print(data)
- if not admin.user == g.wallet.user:
+ if not admin.user == w.wallet.user:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
)
- updated = await update_admin(user=g.wallet.user, **data.dict())
- print(updated)
+ updated = await update_admin(user=w.wallet.user, **data.dict())
+
+ updated.admin_users = removeEmptyString(updated.admin_users.split(','))
+ updated.allowed_users = removeEmptyString(updated.allowed_users.split(','))
+ updated.admin_ext = removeEmptyString(updated.admin_ext.split(','))
+ updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(','))
+ updated.theme = removeEmptyString(updated.theme.split(','))
+ updated.ad_space = removeEmptyString(updated.ad_space.split(','))
+
+ g().admin_conf = g().admin_conf.copy(update=updated.dict())
+
+ print(g().admin_conf)
return {"status": "Success"}
From c299927e7ca1e16c8bc29a98b5af38ed402496de Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 22 Mar 2022 11:42:47 +0000
Subject: [PATCH 0219/1058] success message
---
lnbits/extensions/admin/templates/admin/index.html | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index e9ddc7c4..9aa4f12a 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1273,10 +1273,7 @@
this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- this.wallet.amount +
- ' to ' +
- this.wallet.id,
+ 'Success! Settings changed!',
icon: null
})
})
From 2a63fb191423b2f23de7f70e8197109a290f3689 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 14 Apr 2022 10:42:26 +0100
Subject: [PATCH 0220/1058] allow html to be passed to description
---
lnbits/core/templates/core/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index 68a7b7ed..146fc6ad 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -82,7 +82,7 @@
>
-
{{SITE_DESCRIPTION}}
+
{{SITE_DESCRIPTION | safe}}
From 3fbdac127adf9459d8af4453a802248f7c3d94fc Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 14 Apr 2022 16:37:13 +0100
Subject: [PATCH 0221/1058] update funding wallets
---
lnbits/extensions/admin/crud.py | 12 +
.../admin/templates/admin/index.html | 523 ++++++++++--------
lnbits/extensions/admin/views.py | 3 +-
lnbits/extensions/admin/views_api.py | 19 +-
4 files changed, 315 insertions(+), 242 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index e14ad194..dd39e8e4 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -39,6 +39,18 @@ async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin")
return Admin(**row) if row else None
+async def update_funding(data: Funding) -> Funding:
+ await db.execute(
+ """
+ UPDATE funding
+ SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
+ WHERE id = ?
+ """,
+ (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
+ )
+ row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,))
+ assert row, "Newly updated settings couldn't be retrieved"
+ return Funding(**row) if row else None
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM funding")
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 9aa4f12a..d56b3d79 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -31,11 +31,11 @@
-
-
-
- Wallets Management
-
+
+
+
+ Wallets Management
+
@@ -62,43 +62,96 @@
-
TopUp a wallet
-
-
-
-
-
-
-
-
+
TopUp a wallet
+
Funding Sources
-
+ {% raw %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+ User Management
+
+
+ Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %}
+
+
Admin Users
+ hint="Users with admin privileges"
+ >
{% raw %}
-
-
-
- {% raw %}
-
Allowed Users
+
- {{ user }}
-
- {% endraw %}
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
+
+
+ Server Management
-
-
-
-
-
Disabled Extensions
-
-
-
-
-
- Save
-
-
-
-
-
- Server Management
-
Server Info
{%raw%}
- SQlite: {{data.admin.data_folder}}
- Postgres: {{data.admin.database_url}}
+
+ SQlite: {{data.admin.data_folder}}
+
+
+ Postgres: {{data.admin.database_url}}
+
{%endraw%}
@@ -520,7 +576,10 @@
Hide API
- Hides wallet api, extensions can choose to honor
+ Hides wallet api, extensions can choose to
+ honor
-
-
-
- Save
-
-
-
-
-
- UI Management
-
+
+
+
+ Save
+
+
+
+
+
+ UI Management
+
+
Default Wallet Name
@@ -628,12 +682,15 @@
@keydown.enter="addAdSpace"
type="text"
label="Ad image URL"
- hint="Ad image filepaths or urls, extensions can choose to honor">
+ hint="Ad image filepaths or urls, extensions can choose to honor"
+ >
{% raw %}
-
-
-
-
- Save
-
-
-
-
-
+
+
+
+ Save
+
+
+
+
+
-
-
-Admin
-
+
+
-
-
-
-
- Wallet topup
-
-
-
-
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }}
@@ -1100,14 +1114,22 @@
'LnbitsWallet',
'OpenNodeWallet'
],
-
+
admin: {
edited: [],
- funding: {},
+ funding: [],
senddata: {}
}
},
- themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'],
+ themes: [
+ 'classic',
+ 'bitcoin',
+ 'flamingo',
+ 'mint',
+ 'autumn',
+ 'monochrome',
+ 'salvador'
+ ],
options: [
'bleskomat',
'captcha',
@@ -1139,10 +1161,13 @@
self.cancel.on = true
}
funding = JSON.parse(String('{{ funding | tojson|safe }}'))
- var i
+ funding.map(f => {
+ this.data.admin.funding.push(f)
+ })
+ /*var i
for (i = 0; i < funding.length; i++) {
self.data.admin.funding[funding[i].backend_wallet] = funding[i]
- }
+ }*/
let settings = JSON.parse('{{ settings | tojson|safe }}')
settings.balance = '{{ balance }}'
this.data.admin = {...this.data.admin, ...settings}
@@ -1150,42 +1175,42 @@
console.log(settings)
},
methods: {
- addAdminUser(){
+ addAdminUser() {
let addUser = this.data.admin_users_add
let admin_users = this.data.admin.admin_users
- if(addUser.length && !admin_users.includes(addUser)){
+ if (addUser.length && !admin_users.includes(addUser)) {
admin_users.push(addUser)
this.data.admin.admin_users = admin_users
- this.data.admin_users_add = ""
+ this.data.admin_users_add = ''
}
},
- removeAdminUser(user){
+ removeAdminUser(user) {
let admin_users = this.data.admin.admin_users
this.data.admin.admin_users = admin_users.filter(u => u !== user)
},
- addAllowedUser(){
+ addAllowedUser() {
let addUser = this.data.allowed_users_add
let allowed_users = this.data.admin.allowed_users
- if(addUser.length && !allowed_users.includes(addUser)){
+ if (addUser.length && !allowed_users.includes(addUser)) {
allowed_users.push(addUser)
this.data.admin.allowed_users = allowed_users
- this.data.allowed_users_add = ""
+ this.data.allowed_users_add = ''
}
},
- removeAllowedUser(user){
+ removeAllowedUser(user) {
let allowed_users = this.data.admin.allowed_users
this.data.admin.allowed_users = allowed_users.filter(u => u !== user)
},
- addAdSpace(){
+ addAdSpace() {
let adSpace = this.data.ad_space_add
let spaces = this.data.admin.ad_space
- if(adSpace.length && !spaces.includes(adSpace)){
+ if (adSpace.length && !spaces.includes(adSpace)) {
spaces.push(adSpace)
this.data.admin.ad_space = spaces
- this.data.ad_space_add = ""
+ this.data.ad_space_add = ''
}
},
- removeAdSpace(ad){
+ removeAdSpace(ad) {
let spaces = this.data.admin.ad_space
this.data.admin.ad_space = spaces.filter(s => s !== ad)
},
@@ -1199,14 +1224,14 @@
this.wallet.data.amount,
this.g.user.wallets[0].adminkey
)
- .then((response) => {
+ .then(response => {
this.$q.notify({
type: 'positive',
message:
- 'Success! Added ' +
- this.wallet.data.amount +
- ' to ' +
- this.wallet.data.id,
+ 'Success! Added ' +
+ this.wallet.data.amount +
+ ' to ' +
+ this.wallet.data.id,
icon: null
})
this.wallet.data = {}
@@ -1224,6 +1249,29 @@
self.data.admin.edited.push(source)
console.log(self.data.admin.edited)
},
+ updateFunding(fund) {
+ let data = this.data.admin.funding.find(v => v.backend_wallet == fund)
+
+ LNbits.api
+ .request(
+ 'POST',
+ '/admin/api/v1/admin/funding',
+ this.g.user.wallets[0].adminkey,
+ data
+ )
+ .then(response => {
+ //let wallet = response.data.backend_wallet
+ //this.data.admin.funding[wallet] = response.data
+ //this.data.admin.funding[wallet].endpoint = response.data.endpoint
+ //console.log(this.data.admin.funding)
+ //console.log(this.data.admin)
+ this.$q.notify({
+ type: 'positive',
+ message: `Success! ${response.data.backend_wallet} changed!`,
+ icon: null
+ })
+ })
+ },
UpdateLNbits() {
let {
admin_users,
@@ -1272,8 +1320,7 @@
console.log(response.data)
this.$q.notify({
type: 'positive',
- message:
- 'Success! Settings changed!',
+ message: 'Success! Settings changed!',
icon: null
})
})
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 105f05a1..24b8ca85 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -21,8 +21,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
funding = [f.dict() for f in await get_funding()]
error, balance = await g().WALLET.status()
- print("ADMIN", admin.dict())
- print(g().admin_conf)
+
return admin_renderer().TemplateResponse(
"admin/index.html", {
"request": request,
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 1d4e6a9c..b797dc2d 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -7,11 +7,11 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_wallet
from lnbits.decorators import WalletTypeInfo, require_admin_key
from lnbits.extensions.admin import admin_ext
-from lnbits.extensions.admin.models import Admin, UpdateAdminSettings
+from lnbits.extensions.admin.models import Admin, Funding, UpdateAdminSettings
from lnbits.helpers import removeEmptyString
from lnbits.requestvars import g
-from .crud import get_admin, update_admin, update_wallet_balance
+from .crud import get_admin, update_admin, update_funding, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
@@ -53,3 +53,18 @@ async def api_update_admin(
print(g().admin_conf)
return {"status": "Success"}
+
+@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
+async def api_update_funding(
+ request: Request,
+ data: Funding = Body(...),
+ w: WalletTypeInfo = Depends(require_admin_key)
+ ):
+ admin = await get_admin()
+
+ if not admin.user == w.wallet.user:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
+ funding = await update_funding(data=data)
+ return funding
From a07fbf0187c72b45e0e102951faf3ed6cbfb75fc Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 18 Apr 2022 14:25:06 +0100
Subject: [PATCH 0222/1058] allow user settings without restart
---
lnbits/core/views/generic.py | 7 ++++++-
lnbits/decorators.py | 8 ++++++++
lnbits/helpers.py | 3 +++
3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 31a7b030..83648c44 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -15,7 +15,9 @@ from lnbits.core import db
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.helpers import template_renderer, url_for
+from lnbits.requestvars import g
from lnbits.settings import (
+ LNBITS_ADMIN_UI,
LNBITS_ADMIN_USERS,
LNBITS_ALLOWED_USERS,
LNBITS_CUSTOM_LOGO,
@@ -37,7 +39,6 @@ from ..services import pay_invoice, redeem_lnurl_withdraw
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
-
@core_html_routes.get("/favicon.ico", response_class=FileResponse)
async def favicon():
return FileResponse("lnbits/core/static/favicon.ico")
@@ -119,6 +120,10 @@ async def wallet(
wallet_name = nme
service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+
if not user_id:
user = await get_user((await create_account()).id)
logger.info(f"Create user {user.id}") # type: ignore
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index d4aa63ae..f951163f 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -16,6 +16,7 @@ from lnbits.core.models import User, Wallet
from lnbits.requestvars import g
from lnbits.settings import (
LNBITS_ADMIN_EXTENSIONS,
+ LNBITS_ADMIN_UI,
LNBITS_ADMIN_USERS,
LNBITS_ALLOWED_USERS,
)
@@ -138,6 +139,9 @@ async def get_key_type(
detail="Invoice (or Admin) key required.",
)
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+
for typenr, WalletChecker in zip(
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
):
@@ -231,6 +235,10 @@ async def check_user_exists(usr: UUID4) -> User:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
+
+ if LNBITS_ADMIN_UI:
+ LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
raise HTTPException(
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e456f715..1167143f 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -24,6 +24,9 @@ class Extension(NamedTuple):
class ExtensionManager:
def __init__(self):
+ if settings.LNBITS_ADMIN_UI:
+ settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext
+ settings.LNBITS_ADMIN_EXTENSIONS = g().admin_conf.admin_ext
self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS
self._admin_only: List[str] = [
x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS
From 931286b476bed767e3900a3b60cf10ab962c29e1 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 18 Apr 2022 14:25:26 +0100
Subject: [PATCH 0223/1058] advert for server restart option
---
lnbits/extensions/admin/templates/admin/index.html | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d56b3d79..089c5f1c 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -51,7 +51,9 @@
-
Active Funding
+
+ Active Funding (Requires server restart)
+
Date: Thu, 21 Apr 2022 11:08:26 +0100
Subject: [PATCH 0224/1058] cleanup prints and console logs
---
lnbits/extensions/admin/crud.py | 2 +-
lnbits/extensions/admin/templates/admin/index.html | 4 ++--
lnbits/extensions/admin/views_api.py | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index dd39e8e4..f866bc1a 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -27,7 +27,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
- print("UPDATE", q)
+ # print("UPDATE", q)
await db.execute(
f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 089c5f1c..584d3a33 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1310,7 +1310,7 @@
theme: theme.toString(),
ad_space: ad_space.toString()
}
- console.log(data)
+ //console.log(data)
LNbits.api
.request(
'POST',
@@ -1319,7 +1319,7 @@
data
)
.then(response => {
- console.log(response.data)
+ //console.log(response.data)
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index b797dc2d..c0650c8a 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -35,7 +35,7 @@ async def api_update_admin(
w: WalletTypeInfo = Depends(require_admin_key)
):
admin = await get_admin()
- print(data)
+ # print(data)
if not admin.user == w.wallet.user:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
@@ -51,7 +51,7 @@ async def api_update_admin(
g().admin_conf = g().admin_conf.copy(update=updated.dict())
- print(g().admin_conf)
+ # print(g().admin_conf)
return {"status": "Success"}
@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
From 3f38a9094b9c9e5e3c6b29df67b3508efbc41be6 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 10:49:21 +0100
Subject: [PATCH 0225/1058] create first user on fresh install
---
.env.example | 4 ++--
lnbits/config.py | 3 ++-
lnbits/extensions/admin/README.md | 15 ++++++++-------
lnbits/extensions/admin/migrations.py | 15 ++++++++++++++-
4 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/.env.example b/.env.example
index 4192f82e..f0e21aa8 100644
--- a/.env.example
+++ b/.env.example
@@ -4,8 +4,8 @@ PORT=5000
DEBUG=false
LNBITS_ADMIN_USERS="" # User IDs seperated by comma
-LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS
+LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access
+LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
diff --git a/lnbits/config.py b/lnbits/config.py
index b2fbfff1..3ce51c3c 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -19,6 +19,7 @@ def list_parse_fallback(v):
return v.replace(' ','').split(',')
class Settings(BaseSettings):
+ admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI")
# users
admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
@@ -37,7 +38,7 @@ class Settings(BaseSettings):
site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
- theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS")
+ theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
env: Optional[str]
diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md
index 27729459..6cf073a1 100644
--- a/lnbits/extensions/admin/README.md
+++ b/lnbits/extensions/admin/README.md
@@ -1,11 +1,12 @@
-Example Extension
-*tagline*
-This is an example extension to help you organise and build you own.
+# Admin Extension
-Try to include an image
-
+## Dashboard to manage LNbits from the UI
+With AdminUI you can manage your LNbits from the UI
-If your extension has API endpoints, include useful ones here
+
-curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"
+## Before you start
+
+**This extension doesn't discard the need for the `.env` file!**
+In the .env file, set the `LNBITS_ADMIN_USERS` variable to include at least your user id.
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 574f772d..0e22e667 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -6,9 +6,22 @@ from lnbits.config import conf
from lnbits.helpers import urlsafe_short_hash
+async def get_admin_user():
+ if(conf.admin_users[0]):
+ return conf.admin_users[0]
+ from lnbits.core.crud import create_account, get_user
+ print("Seems like there's no admin users yet. Let's create an account for you!")
+ account = await create_account()
+ user = account.id
+ assert user, "Newly created user couldn't be retrieved"
+ print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ return user
+
+
+
async def m001_create_admin_table(db):
# users/server
- user = conf.admin_users[0]
+ user = await get_admin_user()
admin_users = ",".join(conf.admin_users)
allowed_users = ",".join(conf.allowed_users)
admin_ext = ",".join(conf.admin_ext)
From 0a29fb736093aeca3987122a7a78381b6f760499 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 12:29:58 +0100
Subject: [PATCH 0226/1058] fix schemas for admin
---
lnbits/commands.py | 11 ++++++++---
lnbits/extensions/admin/crud.py | 12 ++++++------
lnbits/extensions/admin/migrations.py | 26 +++++++++++++-------------
3 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 8c39c338..7d9b49e2 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -55,8 +55,12 @@ def bundle_vendored():
async def get_admin_settings():
from lnbits.extensions.admin.models import Admin
- async with core_db.connect() as conn:
+ try:
+ ext_db = importlib.import_module(f"lnbits.extensions.admin").db
+ except:
+ return False
+ async with ext_db.connect() as conn:
if conn.type == SQLITE:
exists = await conn.fetchone(
"SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
@@ -65,11 +69,12 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
+ print("EXISTS", exists)
if not exists:
return False
- row = await conn.fetchone("SELECT * from admin")
-
+ row = await conn.fetchone("SELECT * from admin.admin")
+
return Admin(**row) if row else None
async def migrate_databases():
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index f866bc1a..67fbc614 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -29,30 +29,30 @@ async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
await db.execute(
- f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
+ f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,))
+ row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,))
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
async def get_admin() -> Admin:
- row = await db.fetchone("SELECT * FROM admin")
+ row = await db.fetchone("SELECT * FROM admin.admin")
return Admin(**row) if row else None
async def update_funding(data: Funding) -> Funding:
await db.execute(
"""
- UPDATE funding
+ UPDATE admin.funding
SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
WHERE id = ?
""",
(data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
)
- row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,))
+ row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,))
assert row, "Newly updated settings couldn't be retrieved"
return Funding(**row) if row else None
async def get_funding() -> List[Funding]:
- rows = await db.fetchall("SELECT * FROM funding")
+ rows = await db.fetchall("SELECT * FROM admin.funding")
return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 0e22e667..c94d140b 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -96,7 +96,7 @@ async def m001_create_admin_table(db):
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS admin (
+ CREATE TABLE IF NOT EXISTS admin.admin (
"user" TEXT PRIMARY KEY,
admin_users TEXT,
allowed_users TEXT,
@@ -120,7 +120,7 @@ async def m001_create_admin_table(db):
)
await db.execute(
"""
- INSERT INTO admin (
+ INSERT INTO admin.admin (
"user",
admin_users,
allowed_users,
@@ -171,7 +171,7 @@ async def m001_create_funding_table(db):
# Make the funding table, if it does not already exist
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS funding (
+ CREATE TABLE IF NOT EXISTS admin.funding (
id TEXT PRIMARY KEY,
backend_wallet TEXT,
endpoint TEXT,
@@ -188,7 +188,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, selected)
VALUES (?, ?, ?, ?)
""",
(
@@ -200,7 +200,7 @@ async def m001_create_funding_table(db):
)
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -214,7 +214,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -228,7 +228,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
@@ -244,7 +244,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
@@ -259,7 +259,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
@@ -274,7 +274,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -288,7 +288,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -302,7 +302,7 @@ async def m001_create_funding_table(db):
await db.execute(
"""
- INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
VALUES (?, ?, ?, ?, ?)
""",
(
@@ -317,7 +317,7 @@ async def m001_create_funding_table(db):
## PLACEHOLDER FOR ECLAIR WALLET
# await db.execute(
# """
- # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
+ # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
# VALUES (?, ?, ?, ?, ?)
# """,
# (
From ac74cfaab7e70d8df8229241e040403d473f04e1 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 12:53:47 +0100
Subject: [PATCH 0227/1058] fix sqlite and show user account
---
lnbits/app.py | 1 +
lnbits/commands.py | 2 +-
lnbits/extensions/admin/migrations.py | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index c8f5c60a..2b483758 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -116,6 +116,7 @@ def check_settings(app: FastAPI):
while True:
admin_set = await get_admin_settings()
if admin_set :
+ print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
break
print("ERROR:", admin_set)
await asyncio.sleep(5)
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 7d9b49e2..763a5b90 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -69,7 +69,7 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
- print("EXISTS", exists)
+
if not exists:
return False
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index c94d140b..6c5b507d 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -15,6 +15,7 @@ async def get_admin_user():
user = account.id
assert user, "Newly created user couldn't be retrieved"
print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ conf.admin_users.insert(0, user)
return user
From 1ebd557b1d544f6baab770111a4bf89f31abea66 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 16 May 2022 15:35:04 +0100
Subject: [PATCH 0228/1058] cleanup and info to user on startup
---
lnbits/app.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 2b483758..eaa33136 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -112,13 +112,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
-
while True:
admin_set = await get_admin_settings()
if admin_set :
- print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
break
- print("ERROR:", admin_set)
+ print("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
@@ -128,6 +126,7 @@ def check_settings(app: FastAPI):
admin_set.theme = removeEmptyString(admin_set.theme.split(','))
admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
g().admin_conf = conf.copy(update=admin_set.dict())
+ print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
From e04e24faec18e01c306d86a6bd45f238a74e1d38 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 8 Jun 2022 11:00:43 +0100
Subject: [PATCH 0229/1058] add custom logo
---
lnbits/config.py | 1 +
lnbits/extensions/admin/migrations.py | 58 ++-----------------
lnbits/extensions/admin/models.py | 2 +
.../admin/templates/admin/index.html | 20 +++++--
lnbits/helpers.py | 3 +-
5 files changed, 26 insertions(+), 58 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 3ce51c3c..d07ca044 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -39,6 +39,7 @@ class Settings(BaseSettings):
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
+ custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
env: Optional[str]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 6c5b507d..aad66f02 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -41,60 +41,9 @@ async def m001_create_admin_table(db):
site_description = conf.site_description
default_wallet_name = conf.default_wallet_name
theme = ",".join(conf.theme)
+ custom_logo = conf.custom_logo
ad_space = ",".join(conf.ad_space)
- # if getenv("LNBITS_ADMIN_EXTENSIONS"):
- # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS")
-
- # if getenv("LNBITS_DATABASE_URL"):
- # database_url = getenv("LNBITS_DATABASE_URL")
-
- # if getenv("LNBITS_HIDE_API"):
- # hide_api = getenv("LNBITS_HIDE_API")
-
- # if getenv("LNBITS_THEME_OPTIONS"):
- # theme = getenv("LNBITS_THEME_OPTIONS")
-
- # if getenv("LNBITS_AD_SPACE"):
- # ad_space = getenv("LNBITS_AD_SPACE")
-
- # if getenv("LNBITS_SITE_TITLE"):
- # site_title = getenv("LNBITS_SITE_TITLE")
-
- # if getenv("LNBITS_SITE_TAGLINE"):
- # site_tagline = getenv("LNBITS_SITE_TAGLINE")
-
- # if getenv("LNBITS_SITE_DESCRIPTION"):
- # site_description = getenv("LNBITS_SITE_DESCRIPTION")
-
- # if getenv("LNBITS_ALLOWED_USERS"):
- # allowed_users = getenv("LNBITS_ALLOWED_USERS")
-
- # if getenv("LNBITS_ADMIN_USERS"):
- # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split())
- # user = admin_users.split(',')[0]
-
- # if getenv("LNBITS_DEFAULT_WALLET_NAME"):
- # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
-
- # if getenv("LNBITS_DATA_FOLDER"):
- # data_folder = getenv("LNBITS_DATA_FOLDER")
-
- # if getenv("LNBITS_DISABLED_EXTENSIONS"):
- # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
-
- # if getenv("LNBITS_FORCE_HTTPS"):
- # force_https = getenv("LNBITS_FORCE_HTTPS")
-
- # if getenv("LNBITS_SERVICE_FEE"):
- # service_fee = getenv("LNBITS_SERVICE_FEE")
-
- # if getenv("LNBITS_DENOMINATION"):
- # denomination = getenv("LNBITS_DENOMINATION", "sats")
-
- # if getenv("LNBITS_BACKEND_WALLET_CLASS"):
- # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
-
await db.execute(
"""
CREATE TABLE IF NOT EXISTS admin.admin (
@@ -115,6 +64,7 @@ async def m001_create_admin_table(db):
site_description TEXT,
default_wallet_name TEXT,
theme TEXT,
+ custom_logo TEXT,
ad_space TEXT
);
"""
@@ -139,8 +89,9 @@ async def m001_create_admin_table(db):
site_description,
default_wallet_name,
theme,
+ custom_logo,
ad_space)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
user,
@@ -160,6 +111,7 @@ async def m001_create_admin_table(db):
site_description,
default_wallet_name,
theme,
+ custom_logo,
ad_space,
),
)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 0f25679d..3b17e720 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -23,6 +23,7 @@ class UpdateAdminSettings(BaseModel):
default_wallet_name: str = Query("LNbits wallet")
denomination: str = Query("sats")
theme: str = Query(None)
+ custom_logo: str = Query(None)
ad_space: str = Query(None)
class Admin(BaseModel):
@@ -46,6 +47,7 @@ class Admin(BaseModel):
default_wallet_name: Optional[str]
denomination: str = Field(default="sats")
theme: Optional[str]
+ custom_logo: Optional[str]
ad_space: Optional[str]
@classmethod
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 584d3a33..d9790051 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -705,6 +705,19 @@
+
@@ -718,10 +731,7 @@
-
+
@@ -1292,6 +1323,8 @@
disabled_ext,
funding_source,
force_https,
+ reserve_fee_min,
+ reserve_fee_pct,
service_fee,
hide_api,
site_title,
@@ -1311,6 +1344,8 @@
disabled_ext: disabled_ext.toString(),
funding_source,
force_https,
+ reserve_fee_min,
+ reserve_fee_pct,
service_fee,
hide_api,
site_title,
diff --git a/lnbits/settings.py b/lnbits/settings.py
index ed5c77f7..8e5c321a 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,6 +1,5 @@
import importlib
import subprocess
-from email.policy import default
from os import path
from typing import List
From 929d174ba4bc20c074ff7bfd2741521c846d005b Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 8 Jun 2022 15:38:28 +0100
Subject: [PATCH 0231/1058] calle's semantics
---
lnbits/extensions/admin/templates/admin/index.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 832629bc..d34b9068 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -67,7 +67,7 @@
-
Minimum wallet reserve
+
Fee reserve
From 8e0baf7b2dcc2eb018ed972aba856ff3aca41b18 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Tue, 5 Jul 2022 16:25:02 +0100
Subject: [PATCH 0232/1058] blacked
---
lnbits/extensions/admin/__init__.py | 1 +
lnbits/extensions/admin/crud.py | 23 ++++++++++++---
lnbits/extensions/admin/migrations.py | 10 ++++---
lnbits/extensions/admin/models.py | 2 ++
lnbits/extensions/admin/views.py | 10 ++++---
lnbits/extensions/admin/views_api.py | 41 ++++++++++++++-------------
6 files changed, 56 insertions(+), 31 deletions(-)
diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py
index 6a56b2bb..24b91fe2 100644
--- a/lnbits/extensions/admin/__init__.py
+++ b/lnbits/extensions/admin/__init__.py
@@ -7,6 +7,7 @@ db = Database("ext_admin")
admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"])
+
def admin_renderer():
return template_renderer(["lnbits/extensions/admin/templates"])
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 67fbc614..0d7019cc 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -11,13 +11,13 @@ from .models import Admin, Funding
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
temp_id = f"temp_{urlsafe_short_hash()}"
internal_id = f"internal_{urlsafe_short_hash()}"
-
+
payment = await create_payment(
wallet_id=wallet_id,
checking_id=internal_id,
payment_request="admin_internal",
payment_hash="admin_internal",
- amount=amount*1000,
+ amount=amount * 1000,
memo="Admin top up",
pending=False,
)
@@ -25,6 +25,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
await internal_invoice_queue.put(internal_id)
return payment
+
async def update_admin(user: str, **kwargs) -> Admin:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
@@ -35,23 +36,37 @@ async def update_admin(user: str, **kwargs) -> Admin:
assert row, "Newly updated settings couldn't be retrieved"
return Admin(**row) if row else None
+
async def get_admin() -> Admin:
row = await db.fetchone("SELECT * FROM admin.admin")
return Admin(**row) if row else None
+
async def update_funding(data: Funding) -> Funding:
await db.execute(
"""
UPDATE admin.funding
SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ?
WHERE id = ?
- """,
- (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,),
+ """,
+ (
+ data.backend_wallet,
+ data.endpoint,
+ data.port,
+ data.read_key,
+ data.invoice_key,
+ data.admin_key,
+ data.cert,
+ data.balance,
+ data.selected,
+ data.id,
+ ),
)
row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,))
assert row, "Newly updated settings couldn't be retrieved"
return Funding(**row) if row else None
+
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM admin.funding")
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index f3663435..388f5ec6 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -7,19 +7,21 @@ from lnbits.helpers import urlsafe_short_hash
async def get_admin_user():
- if(conf.admin_users[0]):
+ if conf.admin_users[0]:
return conf.admin_users[0]
from lnbits.core.crud import create_account, get_user
+
print("Seems like there's no admin users yet. Let's create an account for you!")
account = await create_account()
user = account.id
assert user, "Newly created user couldn't be retrieved"
- print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.")
+ print(
+ f"Your newly created account/user id is: {user}. This will be the Super Admin user."
+ )
conf.admin_users.insert(0, user)
return user
-
async def m001_create_admin_table(db):
# users/server
user = await get_admin_user()
@@ -28,7 +30,7 @@ async def m001_create_admin_table(db):
admin_ext = ",".join(conf.admin_ext)
disabled_ext = ",".join(conf.disabled_ext)
funding_source = conf.funding_source
- #operational
+ # operational
data_folder = conf.data_folder
database_url = conf.database_url
force_https = conf.force_https
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 3d8efdcd..6e95d68f 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -28,6 +28,7 @@ class UpdateAdminSettings(BaseModel):
custom_logo: str = Query(None)
ad_space: str = Query(None)
+
class Admin(BaseModel):
# users
user: str
@@ -59,6 +60,7 @@ class Admin(BaseModel):
data = dict(row)
return cls(**data)
+
class Funding(BaseModel):
id: str
backend_wallet: str
diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py
index 24b8ca85..ceda5192 100644
--- a/lnbits/extensions/admin/views.py
+++ b/lnbits/extensions/admin/views.py
@@ -16,19 +16,21 @@ from .crud import get_admin, get_funding
templates = Jinja2Templates(directory="templates")
+
@admin_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
admin = await get_admin()
funding = [f.dict() for f in await get_funding()]
error, balance = await g().WALLET.status()
-
+
return admin_renderer().TemplateResponse(
- "admin/index.html", {
+ "admin/index.html",
+ {
"request": request,
"user": user.dict(),
"admin": admin.dict(),
"funding": funding,
"settings": g().admin_conf.dict(),
- "balance": balance
- }
+ "balance": balance,
+ },
)
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c0650c8a..784ad97f 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -15,16 +15,18 @@ from .crud import get_admin, update_admin, update_funding, update_wallet_balance
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
-async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)):
+async def api_update_balance(
+ wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)
+):
try:
wallet = await get_wallet(wallet_id)
except:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
-
+
return {"status": "Success"}
@@ -32,39 +34,40 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D
async def api_update_admin(
request: Request,
data: UpdateAdminSettings = Body(...),
- w: WalletTypeInfo = Depends(require_admin_key)
- ):
+ w: WalletTypeInfo = Depends(require_admin_key),
+):
admin = await get_admin()
# print(data)
if not admin.user == w.wallet.user:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
updated = await update_admin(user=w.wallet.user, **data.dict())
- updated.admin_users = removeEmptyString(updated.admin_users.split(','))
- updated.allowed_users = removeEmptyString(updated.allowed_users.split(','))
- updated.admin_ext = removeEmptyString(updated.admin_ext.split(','))
- updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(','))
- updated.theme = removeEmptyString(updated.theme.split(','))
- updated.ad_space = removeEmptyString(updated.ad_space.split(','))
+ updated.admin_users = removeEmptyString(updated.admin_users.split(","))
+ updated.allowed_users = removeEmptyString(updated.allowed_users.split(","))
+ updated.admin_ext = removeEmptyString(updated.admin_ext.split(","))
+ updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(","))
+ updated.theme = removeEmptyString(updated.theme.split(","))
+ updated.ad_space = removeEmptyString(updated.ad_space.split(","))
g().admin_conf = g().admin_conf.copy(update=updated.dict())
-
+
# print(g().admin_conf)
return {"status": "Success"}
+
@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK)
async def api_update_funding(
request: Request,
data: Funding = Body(...),
- w: WalletTypeInfo = Depends(require_admin_key)
- ):
+ w: WalletTypeInfo = Depends(require_admin_key),
+):
admin = await get_admin()
if not admin.user == w.wallet.user:
raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
- )
+ status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
+ )
funding = await update_funding(data=data)
return funding
From 429217f5a4876920277ad0719458daca52045b90 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:28:13 +0100
Subject: [PATCH 0233/1058] Had to add a couple of tries
---
lnbits/core/views/generic.py | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 83648c44..63f7af68 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -133,12 +133,19 @@ async def wallet(
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
)
- if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
- return template_renderer().TemplateResponse(
- "error.html", {"request": request, "err": "User not authorized."}
- )
- if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
- user.admin = True
+ try:
+ if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
+ return template_renderer().TemplateResponse(
+ "error.html", {"request": request, "err": "User not authorized."}
+ )
+ except:
+ pass
+
+ try:
+ if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
+ user.admin = True
+ except:
+ pass
if not wallet_id:
if user.wallets and not wallet_name: # type: ignore
wallet = user.wallets[0] # type: ignore
From 1aa2f01d29a25dd5fffac385afd3b5814e10b45c Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:31:31 +0100
Subject: [PATCH 0234/1058] Added couple more tries
---
lnbits/decorators.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index f951163f..a810892d 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -239,13 +239,16 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
-
- if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
- raise HTTPException(
- status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
- )
-
- if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
- g().user.admin = True
-
+ try:
+ if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ except:
+ pass
+ try:
+ if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
+ g().user.admin = True
+ except:
+ pass
return g().user
From 10a065a7ca4f98c725e690d66f9c4f5c60d0c4e3 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:35:06 +0100
Subject: [PATCH 0235/1058] Reverted try
---
lnbits/decorators.py | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index a810892d..904ca1c2 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -239,16 +239,12 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
- try:
- if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
- raise HTTPException(
- status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
- )
- except:
- pass
- try:
- if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
- g().user.admin = True
+ if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
+ g().user.admin = True
except:
pass
return g().user
From 3129692ab16fbd66727faba6fb04c87e56b6e0f7 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 21 Sep 2022 15:37:07 +0100
Subject: [PATCH 0236/1058] reverted other try
---
lnbits/core/views/generic.py | 19 ++++++-------------
lnbits/decorators.py | 2 --
2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 63f7af68..83648c44 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -133,19 +133,12 @@ async def wallet(
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
)
- try:
- if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
- return template_renderer().TemplateResponse(
- "error.html", {"request": request, "err": "User not authorized."}
- )
- except:
- pass
-
- try:
- if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
- user.admin = True
- except:
- pass
+ if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
+ return template_renderer().TemplateResponse(
+ "error.html", {"request": request, "err": "User not authorized."}
+ )
+ if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
+ user.admin = True
if not wallet_id:
if user.wallets and not wallet_name: # type: ignore
wallet = user.wallets[0] # type: ignore
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 904ca1c2..dd26d8fe 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -245,6 +245,4 @@ async def check_user_exists(usr: UUID4) -> User:
)
if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
g().user.admin = True
- except:
- pass
return g().user
From 42f6acd9f4f2f076cf278a843d48db11cbfb2b63 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 21 Sep 2022 18:40:46 +0100
Subject: [PATCH 0237/1058] fix main merge missing settings
---
lnbits/app.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lnbits/app.py b/lnbits/app.py
index eaa33136..176c6bb9 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -56,6 +56,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
},
)
+ if lnbits.settings.LNBITS_ADMIN_UI:
+ g().admin_conf = conf
+ check_settings(app)
+
+ g().WALLET = WALLET
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
"/core/static",
From a6bdd8c575f74685c9f71dfd142c908543b1d210 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 10:46:11 +0200
Subject: [PATCH 0238/1058] format
---
lnbits/app.py | 22 +++++++++++++---------
lnbits/commands.py | 6 +++++-
lnbits/config.py | 25 ++++++++++++++++++-------
lnbits/core/views/generic.py | 1 +
lnbits/decorators.py | 2 +-
lnbits/helpers.py | 8 +++++---
6 files changed, 43 insertions(+), 21 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 176c6bb9..f4e44a0f 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -114,24 +114,28 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
+
def check_settings(app: FastAPI):
@app.on_event("startup")
async def check_settings_admin():
while True:
admin_set = await get_admin_settings()
- if admin_set :
+ if admin_set:
break
print("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
-
- admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(','))
- admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(','))
- admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(','))
- admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(','))
- admin_set.theme = removeEmptyString(admin_set.theme.split(','))
- admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(','))
+
+ admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(","))
+ admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(","))
+ admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(","))
+ admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(","))
+ admin_set.theme = removeEmptyString(admin_set.theme.split(","))
+ admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(","))
g().admin_conf = conf.copy(update=admin_set.dict())
- print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}")
+ print(
+ f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}"
+ )
+
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
diff --git a/lnbits/commands.py b/lnbits/commands.py
index 763a5b90..86868f1f 100644
--- a/lnbits/commands.py
+++ b/lnbits/commands.py
@@ -5,6 +5,7 @@ import re
import warnings
import click
+from genericpath import exists
from loguru import logger
from .core import db as core_db
@@ -52,6 +53,7 @@ def bundle_vendored():
with open(outputpath, "w") as f:
f.write(output)
+
async def get_admin_settings():
from lnbits.extensions.admin.models import Admin
@@ -61,6 +63,7 @@ async def get_admin_settings():
return False
async with ext_db.connect() as conn:
+
if conn.type == SQLITE:
exists = await conn.fetchone(
"SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
@@ -69,7 +72,7 @@ async def get_admin_settings():
exists = await conn.fetchone(
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
)
-
+
if not exists:
return False
@@ -77,6 +80,7 @@ async def get_admin_settings():
return Admin(**row) if row else None
+
async def migrate_databases():
"""Creates the necessary databases if they don't exist already; or migrates them."""
diff --git a/lnbits/config.py b/lnbits/config.py
index 37b700fd..cf26ad21 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -6,17 +6,19 @@ from typing import List, Optional
from pydantic import BaseSettings, Field
wallets_module = importlib.import_module("lnbits.wallets")
-wallet_class = getattr(
+wallet_class = getattr(
wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet")
)
WALLET = wallet_class()
+
def list_parse_fallback(v):
try:
return json.loads(v)
except Exception as e:
- return v.replace(' ','').split(',')
+ return v.replace(" ", "").split(",")
+
class Settings(BaseSettings):
admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI")
@@ -24,7 +26,9 @@ class Settings(BaseSettings):
admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS")
allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS")
admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS")
- disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS")
+ disabled_ext: List[str] = Field(
+ default_factory=list, env="LNBITS_DISABLED_EXTENSIONS"
+ )
funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS")
# ops
data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER")
@@ -37,10 +41,17 @@ class Settings(BaseSettings):
denomination: str = Field(default="sats", env="LNBITS_DENOMINATION")
# Change theme
site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE")
- site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE")
+ site_tagline: str = Field(
+ default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE"
+ )
site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION")
- default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME")
- theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS")
+ default_wallet_name: str = Field(
+ default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME"
+ )
+ theme: List[str] = Field(
+ default=["classic, flamingo, mint, salvador, monochrome, autumn"],
+ env="LNBITS_THEME_OPTIONS",
+ )
custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
@@ -48,7 +59,7 @@ class Settings(BaseSettings):
debug: Optional[str]
host: Optional[str]
port: Optional[str]
- lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
+ lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
# @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True)
# def validate(cls, val):
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 83648c44..3a1fbdfc 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -39,6 +39,7 @@ from ..services import pay_invoice, redeem_lnurl_withdraw
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
+
@core_html_routes.get("/favicon.ico", response_class=FileResponse)
async def favicon():
return FileResponse("lnbits/core/static/favicon.ico")
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index dd26d8fe..58b025aa 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -235,7 +235,7 @@ async def check_user_exists(usr: UUID4) -> User:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
-
+
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index 7bd1b54a..f4255c86 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -157,11 +157,13 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s
url = f"{base}{endpoint}{url_params}"
return url
+
def removeEmptyString(arr):
return list(filter(None, arr))
+
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
- if(settings.LNBITS_ADMIN_UI):
+ if settings.LNBITS_ADMIN_UI:
_ = g().admin_conf
settings.LNBITS_AD_SPACE = _.ad_space
settings.LNBITS_HIDE_API = _.hide_api
@@ -170,8 +172,8 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
settings.LNBITS_SITE_TAGLINE = _.site_tagline
settings.LNBITS_SITE_DESCRIPTION = _.site_description
settings.LNBITS_THEME_OPTIONS = _.theme
- settings.LNBITS_CUSTOM_LOGO = _.custom_logo
-
+ settings.LNBITS_CUSTOM_LOGO = _.custom_logo
+
t = Jinja2Templates(
loader=jinja2.FileSystemLoader(
["lnbits/templates", "lnbits/core/templates", *additional_folders]
From 635bcf66d6f8cf7a65322a41abed92153039cd9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 11:47:24 +0200
Subject: [PATCH 0239/1058] format black
---
lnbits/app.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/app.py b/lnbits/app.py
index f4e44a0f..118bea98 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -56,6 +56,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
},
)
+
if lnbits.settings.LNBITS_ADMIN_UI:
g().admin_conf = conf
check_settings(app)
From 11393ef7e9ed808d9034baa8515eabcd37918a02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 15:29:12 +0200
Subject: [PATCH 0240/1058] fix AD_SPACE
---
lnbits/core/templates/core/wallet.html | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index 4bf6067c..cc45eb68 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -385,12 +385,9 @@
- {% endif %} {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD =
- ADS.split(';') %}
+ {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %}
- {% endfor %} {% endif %}
From 0beb112d5b4f938201adfc6fc2814717ddac20a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 15:55:37 +0200
Subject: [PATCH 0241/1058] fix some javascript errors when adding users
---
lnbits/extensions/admin/templates/admin/index.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d34b9068..1e881cb6 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1221,7 +1221,7 @@
addAdminUser() {
let addUser = this.data.admin_users_add
let admin_users = this.data.admin.admin_users
- if (addUser.length && !admin_users.includes(addUser)) {
+ if (addUser && addUser.length && !admin_users.includes(addUser)) {
admin_users.push(addUser)
this.data.admin.admin_users = admin_users
this.data.admin_users_add = ''
@@ -1234,7 +1234,7 @@
addAllowedUser() {
let addUser = this.data.allowed_users_add
let allowed_users = this.data.admin.allowed_users
- if (addUser.length && !allowed_users.includes(addUser)) {
+ if (addUser && addUser.length && !allowed_users.includes(addUser)) {
allowed_users.push(addUser)
this.data.admin.allowed_users = allowed_users
this.data.allowed_users_add = ''
@@ -1336,7 +1336,6 @@
custom_logo,
ad_space
} = this.data.admin
- //console.log("this", this.data.admin)
let data = {
admin_users: admin_users.toString(),
allowed_users: allowed_users.toString(),
From bfff5f3775cd090b454869c434042b4acdb37434 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 16:04:55 +0200
Subject: [PATCH 0242/1058] fix ADMIN_UI=false errors
---
lnbits/core/views/generic.py | 3 +++
lnbits/decorators.py | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 3a1fbdfc..db4fac43 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -124,6 +124,9 @@ async def wallet(
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+ else:
+ LNBITS_ADMIN_USERS = []
+ LNBITS_ALLOWED_USERS = []
if not user_id:
user = await get_user((await create_account()).id)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 58b025aa..5a3c0a5c 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -141,6 +141,8 @@ async def get_key_type(
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
+ else:
+ LNBITS_ADMIN_USERS = []
for typenr, WalletChecker in zip(
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
@@ -239,6 +241,10 @@ async def check_user_exists(usr: UUID4) -> User:
if LNBITS_ADMIN_UI:
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
+ else:
+ LNBITS_ADMIN_USERS = []
+ LNBITS_ALLOWED_USERS = []
+
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
From cede3317f3655c09b196c5293174a1dbaaaa18ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 16:14:17 +0200
Subject: [PATCH 0243/1058] prettier
---
lnbits/core/templates/core/wallet.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index cc45eb68..5393007d 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -386,8 +386,7 @@
{% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %}
-
- {% endfor %} {% endif %}
From b442acc24f02c91971d29fbfe03dda9d5b5fc34c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 22 Sep 2022 18:35:47 +0200
Subject: [PATCH 0244/1058] fix migration tests
---
lnbits/extensions/admin/migrations.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 388f5ec6..196c9fc0 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -7,7 +7,7 @@ from lnbits.helpers import urlsafe_short_hash
async def get_admin_user():
- if conf.admin_users[0]:
+ if len(conf.admin_users) > 0:
return conf.admin_users[0]
from lnbits.core.crud import create_account, get_user
From 6db5fb16c85d3fd654afd55ee8d6969d243572bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 26 Sep 2022 16:54:19 +0200
Subject: [PATCH 0245/1058] change comments to use multiple lines
---
.env.example | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/.env.example b/.env.example
index f0e21aa8..673470b7 100644
--- a/.env.example
+++ b/.env.example
@@ -3,11 +3,15 @@ PORT=5000
DEBUG=false
-LNBITS_ADMIN_USERS="" # User IDs seperated by comma
-LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access
-LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
+# User IDs seperated by comma
+LNBITS_ADMIN_USERS=""
+# Extensions only admin can access
+LNBITS_ADMIN_EXTENSIONS="ngrok, admin"
+# Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
+LNBITS_ADMIN_UI=false
-LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
+# Restricts access, User IDs seperated by comma
+LNBITS_ALLOWED_USERS=""
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
From affec50a3da3ac812b5046c49483676e93962b2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 26 Sep 2022 16:59:30 +0200
Subject: [PATCH 0246/1058] fix merge error
---
lnbits/app.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 118bea98..82a8f20e 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -57,10 +57,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
},
)
- if lnbits.settings.LNBITS_ADMIN_UI:
- g().admin_conf = conf
- check_settings(app)
-
g().WALLET = WALLET
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
From 7aba2f989cdd78539ca22c69dc1a2e7d5ceb3d37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:14:36 +0200
Subject: [PATCH 0247/1058] use logger in app.py
---
lnbits/app.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 82a8f20e..e07d2ada 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -31,8 +31,6 @@ from .helpers import (
url_for_vendored,
)
from .requestvars import g
-
-# from .settings import WALLET
from .tasks import (
catch_everything_and_restart,
check_pending_payments,
@@ -119,7 +117,7 @@ def check_settings(app: FastAPI):
admin_set = await get_admin_settings()
if admin_set:
break
- print("Waiting for admin settings... retrying in 5 seconds!")
+ logger.info("Waiting for admin settings... retrying in 5 seconds!")
await asyncio.sleep(5)
admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(","))
@@ -129,7 +127,7 @@ def check_settings(app: FastAPI):
admin_set.theme = removeEmptyString(admin_set.theme.split(","))
admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(","))
g().admin_conf = conf.copy(update=admin_set.dict())
- print(
+ logger.info(
f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}"
)
From 212b8f9fdd67c73b16f2c0d97cd549bc7bb8f29e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:14:57 +0200
Subject: [PATCH 0248/1058] fix config
---
lnbits/config.py | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index cf26ad21..874effae 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -17,7 +17,11 @@ def list_parse_fallback(v):
try:
return json.loads(v)
except Exception as e:
- return v.replace(" ", "").split(",")
+ replaced = v.replace(" ", "")
+ if replaced:
+ return replaced.split(",")
+ else:
+ return []
class Settings(BaseSettings):
@@ -48,10 +52,7 @@ class Settings(BaseSettings):
default_wallet_name: str = Field(
default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME"
)
- theme: List[str] = Field(
- default=["classic, flamingo, mint, salvador, monochrome, autumn"],
- env="LNBITS_THEME_OPTIONS",
- )
+ theme: List[str] = Field(default_factory=list, env="LNBITS_THEME_OPTIONS")
custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO")
ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE")
# .env
@@ -74,4 +75,5 @@ class Settings(BaseSettings):
conf = Settings()
+print(conf)
WALLET = wallet_class()
From b6a0a321844ff8a185f728b42f4ede3142334637 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 14:17:20 +0200
Subject: [PATCH 0249/1058] concatenate first migrations script
fixup
---
lnbits/config.py | 1 -
lnbits/extensions/admin/migrations.py | 7 +++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/lnbits/config.py b/lnbits/config.py
index 874effae..fe8dabf9 100644
--- a/lnbits/config.py
+++ b/lnbits/config.py
@@ -75,5 +75,4 @@ class Settings(BaseSettings):
conf = Settings()
-print(conf)
WALLET = wallet_class()
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 196c9fc0..2d48a8e4 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -23,6 +23,8 @@ async def get_admin_user():
async def m001_create_admin_table(db):
+
+
# users/server
user = await get_admin_user()
admin_users = ",".join(conf.admin_users)
@@ -78,7 +80,7 @@ async def m001_create_admin_table(db):
await db.execute(
"""
INSERT INTO admin.admin (
- "user",
+ "user",
admin_users,
allowed_users,
admin_ext,
@@ -126,9 +128,6 @@ async def m001_create_admin_table(db):
),
)
-
-async def m001_create_funding_table(db):
-
funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
# Make the funding table, if it does not already exist
From 1eeb9de7de8c2409f9a1d6b828e051ea640870d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 16:37:08 +0200
Subject: [PATCH 0250/1058] add restart button to frontend
---
.../admin/templates/admin/index.html | 28 ++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 1e881cb6..319ca3f0 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -65,6 +65,14 @@
:options="data.funding_source"
>
+
+
+
Fee reserve
@@ -89,7 +97,7 @@
>
-
+
@@ -1257,6 +1265,24 @@
let spaces = this.data.admin.ad_space
this.data.admin.ad_space = spaces.filter(s => s !== ad)
},
+ restartServer() {
+ LNbits.api
+ .request(
+ 'GET',
+ '/admin/api/v1/admin/restart/',
+ this.g.user.wallets[0].adminkey
+ )
+ .then(response => {
+ this.$q.notify({
+ type: 'positive',
+ message: 'Success! Restarted Server',
+ icon: null
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
topupWallet() {
LNbits.api
.request(
From 82e322ae43658bef7735974324fd3bec7919b756 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:34:52 +0200
Subject: [PATCH 0251/1058] make extension use new settings
---
lnbits/extensions/boltz/boltz.py | 14 ++++++--------
lnbits/extensions/boltz/mempool.py | 15 ++++++---------
lnbits/extensions/boltz/views_api.py | 4 ++--
lnbits/extensions/lndhub/views_api.py | 4 ++--
lnbits/extensions/tpos/views.py | 14 +++++++-------
5 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/lnbits/extensions/boltz/boltz.py b/lnbits/extensions/boltz/boltz.py
index ac99d4f4..424d0bf7 100644
--- a/lnbits/extensions/boltz/boltz.py
+++ b/lnbits/extensions/boltz/boltz.py
@@ -12,7 +12,7 @@ from loguru import logger
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.helpers import urlsafe_short_hash
-from lnbits.settings import BOLTZ_NETWORK, BOLTZ_URL
+from lnbits.settings import settings
from .crud import update_swap_status
from .mempool import (
@@ -33,9 +33,7 @@ from .models import (
)
from .utils import check_balance, get_timestamp, req_wrap
-net = NETWORKS[BOLTZ_NETWORK]
-logger.trace(f"BOLTZ_URL: {BOLTZ_URL}")
-logger.trace(f"Bitcoin Network: {net['name']}")
+net = NETWORKS[settings.boltz_network]
async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
@@ -62,7 +60,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
res = req_wrap(
"post",
- f"{BOLTZ_URL}/createswap",
+ f"{settings.boltz_url}/createswap",
json={
"type": "submarine",
"pairId": "BTC/BTC",
@@ -129,7 +127,7 @@ async def create_reverse_swap(
res = req_wrap(
"post",
- f"{BOLTZ_URL}/createswap",
+ f"{settings.boltz_url}/createswap",
json={
"type": "reversesubmarine",
"pairId": "BTC/BTC",
@@ -409,7 +407,7 @@ def check_boltz_limits(amount):
def get_boltz_pairs():
res = req_wrap(
"get",
- f"{BOLTZ_URL}/getpairs",
+ f"{settings.boltz_url}/getpairs",
headers={"Content-Type": "application/json"},
)
return res.json()
@@ -418,7 +416,7 @@ def get_boltz_pairs():
def get_boltz_status(boltzid):
res = req_wrap(
"post",
- f"{BOLTZ_URL}/swapstatus",
+ f"{settings.boltz_url}/swapstatus",
json={"id": boltzid},
)
return res.json()
diff --git a/lnbits/extensions/boltz/mempool.py b/lnbits/extensions/boltz/mempool.py
index a44c0f02..a64cadad 100644
--- a/lnbits/extensions/boltz/mempool.py
+++ b/lnbits/extensions/boltz/mempool.py
@@ -7,14 +7,11 @@ import websockets
from embit.transaction import Transaction
from loguru import logger
-from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL, BOLTZ_MEMPOOL_SPACE_URL_WS
+from lnbits.settings import settings
from .utils import req_wrap
-logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}")
-logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}")
-
-websocket_url = f"{BOLTZ_MEMPOOL_SPACE_URL_WS}/api/v1/ws"
+websocket_url = f"{settings.boltz_mempool_space_url_ws}/api/v1/ws"
async def wait_for_websocket_message(send, message_string):
@@ -33,7 +30,7 @@ async def wait_for_websocket_message(send, message_string):
def get_mempool_tx(address):
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/address/{address}/txs",
+ f"{settings.boltz_mempool_space_url}/api/address/{address}/txs",
headers={"Content-Type": "text/plain"},
)
txs = res.json()
@@ -70,7 +67,7 @@ def get_fee_estimation() -> int:
def get_mempool_fees() -> int:
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/v1/fees/recommended",
+ f"{settings.boltz_mempool_space_url}/api/v1/fees/recommended",
headers={"Content-Type": "text/plain"},
)
fees = res.json()
@@ -80,7 +77,7 @@ def get_mempool_fees() -> int:
def get_mempool_blockheight() -> int:
res = req_wrap(
"get",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/blocks/tip/height",
+ f"{settings.boltz_mempool_space_url}/api/blocks/tip/height",
headers={"Content-Type": "text/plain"},
)
return int(res.text)
@@ -91,7 +88,7 @@ async def send_onchain_tx(tx: Transaction):
logger.debug(f"Boltz - mempool sending onchain tx...")
req_wrap(
"post",
- f"{BOLTZ_MEMPOOL_SPACE_URL}/api/tx",
+ f"{settings.boltz_mempool_space_url}/api/tx",
headers={"Content-Type": "text/plain"},
content=raw,
)
diff --git a/lnbits/extensions/boltz/views_api.py b/lnbits/extensions/boltz/views_api.py
index a4b7d318..18ca14cb 100644
--- a/lnbits/extensions/boltz/views_api.py
+++ b/lnbits/extensions/boltz/views_api.py
@@ -14,7 +14,7 @@ from starlette.requests import Request
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL
+from lnbits.settings import settings
from . import boltz_ext
from .boltz import (
@@ -55,7 +55,7 @@ from .utils import check_balance
response_model=str,
)
async def api_mempool_url():
- return BOLTZ_MEMPOOL_SPACE_URL
+ return settings.boltz_mempool_space_url
# NORMAL SWAP
diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py
index 8cbe5a6b..b2328c39 100644
--- a/lnbits/extensions/lndhub/views_api.py
+++ b/lnbits/extensions/lndhub/views_api.py
@@ -12,7 +12,7 @@ from lnbits import bolt11
from lnbits.core.crud import delete_expired_invoices, get_payments
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.decorators import WalletTypeInfo
-from lnbits.settings import LNBITS_SITE_TITLE, WALLET
+from lnbits.settings import WALLET, settings
from . import lndhub_ext
from .decorators import check_wallet, require_admin_key
@@ -56,7 +56,7 @@ async def lndhub_addinvoice(
_, pr = await create_invoice(
wallet_id=wallet.wallet.id,
amount=int(data.amt),
- memo=data.memo or LNBITS_SITE_TITLE,
+ memo=data.memo or settings.lnbits_site_title,
extra={"tag": "lndhub"},
)
except:
diff --git a/lnbits/extensions/tpos/views.py b/lnbits/extensions/tpos/views.py
index e1f1d21e..dac129a9 100644
--- a/lnbits/extensions/tpos/views.py
+++ b/lnbits/extensions/tpos/views.py
@@ -8,7 +8,7 @@ from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
-from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE
+from lnbits.settings import settings
from . import tpos_ext, tpos_renderer
from .crud import get_tpos
@@ -50,12 +50,12 @@ async def manifest(tpos_id: str):
)
return {
- "short_name": LNBITS_SITE_TITLE,
- "name": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "short_name": settings.lnbits_site_title,
+ "name": tpos.name + " - " + settings.lnbits_site_title,
"icons": [
{
- "src": LNBITS_CUSTOM_LOGO
- if LNBITS_CUSTOM_LOGO
+ "src": settings.lnbits_custom_logo
+ if settings.lnbits_custom_logo
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
"type": "image/png",
"sizes": "900x900",
@@ -69,9 +69,9 @@ async def manifest(tpos_id: str):
"theme_color": "#1F2234",
"shortcuts": [
{
- "name": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "name": tpos.name + " - " + settings.lnbits_site_title,
"short_name": tpos.name,
- "description": tpos.name + " - " + LNBITS_SITE_TITLE,
+ "description": tpos.name + " - " + settings.lnbits_site_title,
"url": "/tpos/" + tpos_id,
}
],
From 5aa9cdd45631379341f31d17d9484c5e3ad05a4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:35:26 +0200
Subject: [PATCH 0252/1058] remove enviroms
---
pyproject.toml | 1 -
1 file changed, 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 7418de27..92c43dce 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,7 +20,6 @@ charset-normalizer = "2.0.6"
click = "8.0.1"
ecdsa = "0.17.0"
embit = "0.4.9"
-environs = "9.3.3"
fastapi = "0.78.0"
h11 = "0.12.0"
httpcore = "0.15.0"
From 6a2c7414783a5edb4b7932355f3fe145053332a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 3 Oct 2022 16:36:14 +0200
Subject: [PATCH 0253/1058] fix admin
---
lnbits/extensions/admin/crud.py | 24 +-
lnbits/extensions/admin/migrations.py | 337 +----
lnbits/extensions/admin/models.py | 58 +-
.../admin/templates/admin/_tab_funding.html | 158 +++
.../admin/templates/admin/_tab_server.html | 78 ++
.../admin/templates/admin/_tab_theme.html | 122 ++
.../admin/templates/admin/_tab_users.html | 96 ++
.../admin/templates/admin/index.html | 1133 +----------------
lnbits/extensions/admin/views.py | 16 +-
lnbits/extensions/admin/views_api.py | 28 +-
10 files changed, 576 insertions(+), 1474 deletions(-)
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_funding.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_server.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_theme.html
create mode 100644 lnbits/extensions/admin/templates/admin/_tab_users.html
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index 0d7019cc..e4cb5d77 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -2,10 +2,11 @@ from typing import List
from lnbits.core.crud import create_payment
from lnbits.helpers import urlsafe_short_hash
+from lnbits.settings import Settings
from lnbits.tasks import internal_invoice_queue
from . import db
-from .models import Admin, Funding
+from .models import Funding
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -23,26 +24,26 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
)
# manually send this for now
await internal_invoice_queue.put(internal_id)
- return payment
-async def update_admin(user: str, **kwargs) -> Admin:
+async def update_settings(user: str, **kwargs) -> Settings:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
# print("UPDATE", q)
await db.execute(
- f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
+ f'UPDATE admin.settings SET {q} WHERE "user" = ?', (*kwargs.values(), user)
)
- row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,))
+ row = await db.fetchone('SELECT * FROM admin.settings WHERE "user" = ?', (user,))
assert row, "Newly updated settings couldn't be retrieved"
- return Admin(**row) if row else None
-
-
-async def get_admin() -> Admin:
- row = await db.fetchone("SELECT * FROM admin.admin")
- return Admin(**row) if row else None
+ return Settings(**row) if row else None
async def update_funding(data: Funding) -> Funding:
+ await db.execute(
+ """
+ UPDATE admin.settings SET funding_source = ? WHERE user = ?
+ """,
+ (data.backend_wallet, data.user),
+ )
await db.execute(
"""
UPDATE admin.funding
@@ -69,5 +70,4 @@ async def update_funding(data: Funding) -> Funding:
async def get_funding() -> List[Funding]:
rows = await db.fetchall("SELECT * FROM admin.funding")
-
return [Funding(**row) for row in rows]
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index 2d48a8e4..8f6c76a0 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -1,292 +1,57 @@
-from os import getenv
-
-from sqlalchemy.exc import OperationalError # type: ignore
-
-from lnbits.config import conf
-from lnbits.helpers import urlsafe_short_hash
-
-
-async def get_admin_user():
- if len(conf.admin_users) > 0:
- return conf.admin_users[0]
- from lnbits.core.crud import create_account, get_user
-
- print("Seems like there's no admin users yet. Let's create an account for you!")
- account = await create_account()
- user = account.id
- assert user, "Newly created user couldn't be retrieved"
- print(
- f"Your newly created account/user id is: {user}. This will be the Super Admin user."
- )
- conf.admin_users.insert(0, user)
- return user
-
-
-async def m001_create_admin_table(db):
-
-
- # users/server
- user = await get_admin_user()
- admin_users = ",".join(conf.admin_users)
- allowed_users = ",".join(conf.allowed_users)
- admin_ext = ",".join(conf.admin_ext)
- disabled_ext = ",".join(conf.disabled_ext)
- funding_source = conf.funding_source
- # operational
- data_folder = conf.data_folder
- database_url = conf.database_url
- force_https = conf.force_https
- reserve_fee_min = conf.reserve_fee_min
- reserve_fee_pct = conf.reserve_fee_pct
- service_fee = conf.service_fee
- hide_api = conf.hide_api
- denomination = conf.denomination
- # Theme'ing
- site_title = conf.site_title
- site_tagline = conf.site_tagline
- site_description = conf.site_description
- default_wallet_name = conf.default_wallet_name
- theme = ",".join(conf.theme)
- custom_logo = conf.custom_logo
- ad_space = ",".join(conf.ad_space)
-
+async def m001_create_admin_settings_table(db):
await db.execute(
"""
- CREATE TABLE IF NOT EXISTS admin.admin (
- "user" TEXT PRIMARY KEY,
- admin_users TEXT,
- allowed_users TEXT,
- admin_ext TEXT,
- disabled_ext TEXT,
- funding_source TEXT,
- data_folder TEXT,
- database_url TEXT,
- force_https BOOLEAN,
- reserve_fee_min INT,
- reserve_fee_pct REAL,
- service_fee REAL,
- hide_api BOOLEAN,
- denomination TEXT,
- site_title TEXT,
- site_tagline TEXT,
- site_description TEXT,
- default_wallet_name TEXT,
- theme TEXT,
- custom_logo TEXT,
- ad_space TEXT
- );
- """
- )
- await db.execute(
- """
- INSERT INTO admin.admin (
- "user",
- admin_users,
- allowed_users,
- admin_ext,
- disabled_ext,
- funding_source,
- data_folder,
- database_url,
- force_https,
- reserve_fee_min,
- reserve_fee_pct,
- service_fee,
- hide_api,
- denomination,
- site_title,
- site_tagline,
- site_description,
- default_wallet_name,
- theme,
- custom_logo,
- ad_space)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- user,
- admin_users,
- allowed_users,
- admin_ext,
- disabled_ext,
- funding_source,
- data_folder,
- database_url,
- force_https,
- reserve_fee_min,
- reserve_fee_pct,
- service_fee,
- hide_api,
- denomination,
- site_title,
- site_tagline,
- site_description,
- default_wallet_name,
- theme,
- custom_logo,
- ad_space,
- ),
- )
-
- funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
-
- # Make the funding table, if it does not already exist
- await db.execute(
- """
- CREATE TABLE IF NOT EXISTS admin.funding (
- id TEXT PRIMARY KEY,
- backend_wallet TEXT,
- endpoint TEXT,
+ CREATE TABLE IF NOT EXISTS admin.settings (
+ lnbits_admin_ui TEXT,
+ debug TEXT,
+ host TEXT,
port INT,
- read_key TEXT,
- invoice_key TEXT,
- admin_key TEXT,
- cert TEXT,
- balance INT,
- selected INT
+ lnbits_path TEXT,
+ lnbits_commit TEXT,
+ lnbits_admin_users TEXT,
+ lnbits_allowed_users TEXT,
+ lnbits_allowed_funding_sources TEXT,
+ lnbits_admin_extensions TEXT,
+ lnbits_disabled_extensions TEXT,
+ lnbits_site_title TEXT,
+ lnbits_site_tagline TEXT,
+ lnbits_site_description TEXT,
+ lnbits_default_wallet_name TEXT,
+ lnbits_theme_options TEXT,
+ lnbits_custom_logo TEXT,
+ lnbits_ad_space TEXT,
+ lnbits_data_folder TEXT,
+ lnbits_database_url TEXT,
+ lnbits_force_https TEXT,
+ lnbits_reserve_fee_min TEXT,
+ lnbits_reserve_fee_percent TEXT,
+ lnbits_service_fee TEXT,
+ lnbits_hide_api TEXT,
+ lnbits_denomination TEXT,
+ lnbits_backend_wallet_class TEXT,
+ fake_wallet_secret TEXT,
+ lnbits_endpoint TEXT,
+ lnbits_key TEXT,
+ cliche_endpoint TEXT,
+ corelightning_rpc TEXT,
+ eclair_url TEXT,
+ eclair_pass TEXT,
+ lnd_rest_endpoint TEXT,
+ lnd_rest_cert TEXT,
+ lnd_rest_macaroon TEXT,
+ lnpay_api_endpoint TEXT,
+ lnpay_api_key TEXT,
+ lnpay_wallet_key TEXT,
+ lntxbot_api_endpoint TEXT,
+ lntxbot_key TEXT,
+ opennode_api_endpoint TEXT,
+ opennode_key TEXT,
+ spark_url TEXT,
+ spark_token TEXT,
+ boltz_network TEXT,
+ boltz_url TEXT,
+ boltz_mempool_space_url TEXT,
+ boltz_mempool_space_url_ws TEXT
);
"""
)
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, selected)
- VALUES (?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "CLightningWallet",
- getenv("CLIGHTNING_RPC"),
- 1 if funding_wallet == "CLightningWallet" else 0,
- ),
- )
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "SparkWallet",
- getenv("SPARK_URL"),
- getenv("SPARK_TOKEN"),
- 1 if funding_wallet == "SparkWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LnbitsWallet",
- getenv("LNBITS_ENDPOINT"),
- getenv("LNBITS_KEY"),
- 1 if funding_wallet == "LnbitsWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LndWallet",
- getenv("LND_GRPC_ENDPOINT"),
- getenv("LND_GRPC_PORT"),
- getenv("LND_GRPC_MACAROON"),
- getenv("LND_GRPC_CERT"),
- 1 if funding_wallet == "LndWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LndRestWallet",
- getenv("LND_REST_ENDPOINT"),
- getenv("LND_REST_MACAROON"),
- getenv("LND_REST_CERT"),
- 1 if funding_wallet == "LndWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected)
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LNPayWallet",
- getenv("LNPAY_API_ENDPOINT"),
- getenv("LNPAY_WALLET_KEY"),
- getenv("LNPAY_API_KEY"), # this is going in as the cert
- 1 if funding_wallet == "LNPayWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "LntxbotWallet",
- getenv("LNTXBOT_API_ENDPOINT"),
- getenv("LNTXBOT_KEY"),
- 1 if funding_wallet == "LntxbotWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "OpenNodeWallet",
- getenv("OPENNODE_API_ENDPOINT"),
- getenv("OPENNODE_KEY"),
- 1 if funding_wallet == "OpenNodeWallet" else 0,
- ),
- )
-
- await db.execute(
- """
- INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- VALUES (?, ?, ?, ?, ?)
- """,
- (
- urlsafe_short_hash(),
- "SparkWallet",
- getenv("SPARK_URL"),
- getenv("SPARK_TOKEN"),
- 1 if funding_wallet == "SparkWallet" else 0,
- ),
- )
-
- ## PLACEHOLDER FOR ECLAIR WALLET
- # await db.execute(
- # """
- # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected)
- # VALUES (?, ?, ?, ?, ?)
- # """,
- # (
- # urlsafe_short_hash(),
- # "EclairWallet",
- # getenv("ECLAIR_URL"),
- # getenv("ECLAIR_PASS"),
- # 1 if funding_wallet == "EclairWallet" else 0,
- # ),
- # )
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 6e95d68f..ef57cadd 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -29,36 +29,36 @@ class UpdateAdminSettings(BaseModel):
ad_space: str = Query(None)
-class Admin(BaseModel):
- # users
- user: str
- admin_users: Optional[str]
- allowed_users: Optional[str]
- admin_ext: Optional[str]
- disabled_ext: Optional[str]
- funding_source: Optional[str]
- # ops
- data_folder: Optional[str]
- database_url: Optional[str]
- force_https: bool = Field(default=True)
- reserve_fee_min: Optional[int]
- reserve_fee_pct: Optional[float]
- service_fee: float = Optional[float]
- hide_api: bool = Field(default=False)
- # Change theme
- site_title: Optional[str]
- site_tagline: Optional[str]
- site_description: Optional[str]
- default_wallet_name: Optional[str]
- denomination: str = Field(default="sats")
- theme: Optional[str]
- custom_logo: Optional[str]
- ad_space: Optional[str]
+# class Admin(BaseModel):
+# # users
+# user: str
+# admin_users: Optional[str]
+# allowed_users: Optional[str]
+# admin_ext: Optional[str]
+# disabled_ext: Optional[str]
+# funding_source: Optional[str]
+# # ops
+# data_folder: Optional[str]
+# database_url: Optional[str]
+# force_https: bool = Field(default=True)
+# reserve_fee_min: Optional[int]
+# reserve_fee_pct: Optional[float]
+# service_fee: float = Optional[float]
+# hide_api: bool = Field(default=False)
+# # Change theme
+# site_title: Optional[str]
+# site_tagline: Optional[str]
+# site_description: Optional[str]
+# default_wallet_name: Optional[str]
+# denomination: str = Field(default="sats")
+# theme: Optional[str]
+# custom_logo: Optional[str]
+# ad_space: Optional[str]
- @classmethod
- def from_row(cls, row: Row) -> "Admin":
- data = dict(row)
- return cls(**data)
+# @classmethod
+# def from_row(cls, row: Row) -> "Admin":
+# data = dict(row)
+# return cls(**data)
class Funding(BaseModel):
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
new file mode 100644
index 00000000..2ed0aae2
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -0,0 +1,158 @@
+
+
+ Wallets Management
+
+
+
+
+
Funding Source Info
+
+ {%raw%}
+
+ Funding Source: {{data.settings.lnbits_backend_wallet_class}}
+
+ Balance: {{data.balance / 1000}} sats
+ {%endraw%}
+
+
+
+
+
+
+
+
+
Active Funding (Requires server restart)
+
+
+
+
+
+
+
+
+
+
+
TopUp a wallet
+
+
+
+
+
+
+
+
Funding Sources
+ {% raw %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_server.html b/lnbits/extensions/admin/templates/admin/_tab_server.html
new file mode 100644
index 00000000..2924e6a4
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_server.html
@@ -0,0 +1,78 @@
+
+
+ Server Management
+
+
+
+
+
Server Info
+
+ {%raw%}
+
+ SQlite: {{data.settings.lnbits_data_folder}}
+
+
+ Postgres: {{data.settings.lnbits_database_url}}
+
+ {%endraw%}
+
+
+
+
+
+
+
+
Miscelaneous
+
+
+ Force HTTPS
+ Prefer secure URLs
+
+
+
+
+
+
+
+ Hide API
+ Hides wallet api, extensions can choose to honor
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html
new file mode 100644
index 00000000..41dc0447
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html
@@ -0,0 +1,122 @@
+
+
+ UI Management
+
+
+
+
+
+
+
+
Default Wallet Name
+
+
+
+
+
+
+
+
+
Advertisement Slots
+
+
+
+
+ {% raw %}
+
+ {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html
new file mode 100644
index 00000000..3eb53ac4
--- /dev/null
+++ b/lnbits/extensions/admin/templates/admin/_tab_users.html
@@ -0,0 +1,96 @@
+
+
+ User Management
+
+
+ Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{%
+ endraw %}
+
+
+
+
Admin Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
Allowed Users
+
+
+
+
+ {% raw %}
+
+ {{ user }}
+
+ {% endraw %}
+
+
+
+
+
+
+
Disabled Extensions
+
+
+
+
+
+ Save
+
+
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 319ca3f0..87e89321 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -29,1118 +29,16 @@
-
-
-
- Wallets Management
-
-
-
-
-
Funding Source Info
-
- {%raw%}
- Funding Source: {{data.admin.funding_source}}
- Balance: {{data.admin.balance / 1000}} sats
- {%endraw%}
-
-
-
-
-
-
-
-
-
- Active Funding
- (Requires server restart)
-
-
-
-
-
-
-
-
-
-
-
-
-
TopUp a wallet
-
-
-
-
-
-
-
-
-
Funding Sources
- {% raw %}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% endraw %}
-
-
-
-
-
-
- User Management
-
-
- Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %}
-
-
-
-
Admin Users
-
-
-
-
- {% raw %}
-
- {{ user }}
-
- {% endraw %}
-
-
-
-
-
Allowed Users
-
-
-
-
- {% raw %}
-
- {{ user }}
-
- {% endraw %}
-
-
-
-
-
-
-
Disabled Extensions
-
-
-
-
-
- Save
-
-
-
-
-
- Server Management
-
-
-
-
-
Server Info
-
- {%raw%}
-
- SQlite: {{data.admin.data_folder}}
-
-
- Postgres: {{data.admin.database_url}}
-
- {%endraw%}
-
-
-
-
-
-
-
-
Miscelaneous
-
-
- Force HTTPS
- Prefer secure URLs
-
-
-
-
-
-
-
- Hide API
- Hides wallet api, extensions can choose to
- honor
-
-
-
-
-
-
-
-
-
-
-
- Save
-
-
-
-
-
- UI Management
-
-
-
-
-
-
-
-
Default Wallet Name
-
-
-
-
-
-
-
-
-
Advertisement Slots
-
-
-
-
- {% raw %}
-
- {{ space.slice(0, 8) + " ... " + space.slice(-8) }}
-
- {% endraw %}
-
-
-
-
-
-
-
-
- Save
-
-
-
+ {% include "admin/_tab_funding.html" %} {% include
+ "admin/_tab_users.html" %} {% include "admin/_tab_server.html" %} {%
+ include "admin/_tab_theme.html" %}
-
-
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }}
{% endblock %}
From 04b37458983094ff9deec4ba010ec2a93c002cd9 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 7 Oct 2022 19:24:07 +0100
Subject: [PATCH 0279/1058] make saving possible (possible will change in
future)
---
.../admin/templates/admin/index.html | 36 ++++++++++++-------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 72352651..18df16a9 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -121,8 +121,11 @@
created: function () {
this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data
this.balance = +'{{ balance|safe }}'
- this.formData = this.settings //model
+ this.formData = _.clone(this.settings) //model
+ //this.formData.lnbits_ad_space = "hdh"
console.log(this.formData)
+ console.log(_.isEqual(this.settings, this.formData))
+
},
methods: {
addAdminUser() {
@@ -206,18 +209,27 @@
},
updateSettings() {
let data = {
- ...this.settings,
- ...this.formData
+ lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class,
+ lnbits_admin_users: this.formData.lnbits_admin_users.toString(),
+ lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(),
+ lnbits_admin_ext: this.formData.lnbits_admin_ext,
+ lnbits_disabled_ext: this.formData.lnbits_disabled_ext,
+ lnbits_funding_source: this.formData.lnbits_funding_source,
+ lnbits_force_https: this.formData.lnbits_force_https,
+ lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min,
+ lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent,
+ lnbits_service_fee: this.formData.lnbits_service_fee,
+ lnbits_hide_api: this.formData.lnbits_hide_api,
+ lnbits_site_title: this.formData.lnbits_site_title,
+ lnbits_site_tagline: this.formData.lnbits_site_tagline,
+ lnbits_site_description: this.formData.lnbits_site_description,
+ lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name,
+ lnbits_denomination: this.formData.lnbits_denomination,
+ lnbits_theme: this.formData.lnbits_theme,
+ lnbits_custom_logo: this.formData.lnbits_custom_logo,
+ lnbits_ad_space: this.formData.lnbits_ad_space.toString()
}
- /*
- const formElement = document.getElementById('settings_form')
- const formData = new FormData(formElement)
- const data = {}
- formData.forEach((value, key) => (data[key] = value))
- // only for debugging
- for (const [key, value] of formData) {
- console.log(`${key}: ${value}\n`)
- }*/
+ console.log(data)
LNbits.api
.request(
'PUT',
From cc42df12f4d3927c854675f114aff0b75bef6d19 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 7 Oct 2022 19:44:03 +0100
Subject: [PATCH 0280/1058] some more refining
---
lnbits/extensions/admin/models.py | 10 +++++-----
.../extensions/admin/templates/admin/_tab_users.html | 2 ++
lnbits/extensions/admin/templates/admin/index.html | 12 +++++++++---
3 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 13a6cd23..45cd990d 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -4,10 +4,10 @@ from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: str = Query(None)
- lnbits_allowed_users: str = Query(None)
- lnbits_admin_ext: str = Query(None)
- lnbits_disabled_ext: str = Query(None)
+ lnbits_admin_users: str = Query(None) #this should be List[str] ??
+ lnbits_allowed_users: str = Query(None) #this should be List[str] ??
+ lnbits_admin_ext: str = Query(None) #this should be List[str] ??
+ lnbits_disabled_ext: str = Query(None) #this should be List[str] ??
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -21,4 +21,4 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: str = Query(None)
+ lnbits_ad_space: str = Query(None) #this should be List[str] ??
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html
index c396ba7d..08b08a62 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_users.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_users.html
@@ -71,6 +71,7 @@
multiple
hint="Extensions only user with admin privileges can use"
label="Admin extensions"
+ :options="g.extensions.map(e => e.name)"
>
@@ -79,6 +80,7 @@
-
+
Date: Mon, 10 Oct 2022 12:17:35 +0100
Subject: [PATCH 0281/1058] get saved data and alert when data changed
---
.../admin/templates/admin/index.html | 18 +++++++-----------
lnbits/extensions/admin/views_api.py | 5 +++--
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 4754656d..4e401cb4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -3,7 +3,7 @@
-
+
{
+ this.settings = response.data.settings
+ this.formData = _.clone(this.settings)
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c8120564..c2079e37 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -43,8 +43,9 @@ async def api_update_settings(
user: User = Depends(check_admin),
data: UpdateSettings = Body(...),
):
- await update_settings(data)
- return {"status": "Success"}
+ settings = await update_settings(data)
+ logger.debug(settings)
+ return {"status": "Success", "settings": settings.dict()}
@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK)
From 001f6589bb3c173c9ada0f86ec0a2eca08f3b051 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Mon, 10 Oct 2022 12:23:19 +0100
Subject: [PATCH 0282/1058] cleanup and typing fix for data
---
lnbits/extensions/admin/models.py | 12 +++++-----
.../admin/templates/admin/index.html | 22 ++-----------------
lnbits/extensions/admin/views_api.py | 1 -
3 files changed, 9 insertions(+), 26 deletions(-)
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 45cd990d..94fa56bb 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,13 +1,15 @@
+from typing import List
+
from fastapi import Query
from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: str = Query(None) #this should be List[str] ??
- lnbits_allowed_users: str = Query(None) #this should be List[str] ??
- lnbits_admin_ext: str = Query(None) #this should be List[str] ??
- lnbits_disabled_ext: str = Query(None) #this should be List[str] ??
+ lnbits_admin_users: List[str] = Query(None)
+ lnbits_allowed_users: List[str] = Query(None)
+ lnbits_admin_ext: List[str] = Query(None)
+ lnbits_disabled_ext: List[str] = Query(None)
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -21,4 +23,4 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: str = Query(None) #this should be List[str] ??
+ lnbits_ad_space: List[str] = Query(None)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 4e401cb4..d8111595 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -209,26 +209,8 @@
})
},
updateSettings() {
- let data = {
- lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class,
- lnbits_admin_users: this.formData.lnbits_admin_users.toString(),
- lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(),
- lnbits_admin_ext: this.formData.lnbits_admin_ext,
- lnbits_disabled_ext: this.formData.lnbits_disabled_ext,
- lnbits_funding_source: this.formData.lnbits_funding_source,
- lnbits_force_https: this.formData.lnbits_force_https,
- lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min,
- lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent,
- lnbits_service_fee: this.formData.lnbits_service_fee,
- lnbits_hide_api: this.formData.lnbits_hide_api,
- lnbits_site_title: this.formData.lnbits_site_title,
- lnbits_site_tagline: this.formData.lnbits_site_tagline,
- lnbits_site_description: this.formData.lnbits_site_description,
- lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name,
- lnbits_denomination: this.formData.lnbits_denomination,
- lnbits_theme: this.formData.lnbits_theme,
- lnbits_custom_logo: this.formData.lnbits_custom_logo,
- lnbits_ad_space: this.formData.lnbits_ad_space.toString()
+ let data = {
+ ...this.formData
}
LNbits.api
.request(
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index c2079e37..19b52e35 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -44,7 +44,6 @@ async def api_update_settings(
data: UpdateSettings = Body(...),
):
settings = await update_settings(data)
- logger.debug(settings)
return {"status": "Success", "settings": settings.dict()}
From 1cc54ff4b7e19366499743fc9cb881bb58ccf2c6 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Wed, 12 Oct 2022 19:04:46 +0100
Subject: [PATCH 0283/1058] add funding sources options
---
lnbits/app.py | 1 +
lnbits/extensions/admin/models.py | 41 +++-
.../admin/templates/admin/_tab_funding.html | 25 +-
.../admin/templates/admin/index.html | 217 +++++++++++++++++-
4 files changed, 259 insertions(+), 25 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index a8371950..50f218b7 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -84,6 +84,7 @@ async def check_funding_source() -> None:
def signal_handler(signal, frame):
logger.debug(f"SIGINT received, terminating LNbits.")
sys.exit(1)
+
signal.signal(signal.SIGINT, signal_handler)
WALLET = get_wallet_class()
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index 94fa56bb..d9d2b22f 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -6,10 +6,10 @@ from pydantic import BaseModel
class UpdateSettings(BaseModel):
lnbits_backend_wallet_class: str = Query(None)
- lnbits_admin_users: List[str] = Query(None)
- lnbits_allowed_users: List[str] = Query(None)
- lnbits_admin_ext: List[str] = Query(None)
- lnbits_disabled_ext: List[str] = Query(None)
+ lnbits_admin_users: List[str] = Query(None)
+ lnbits_allowed_users: List[str] = Query(None)
+ lnbits_admin_ext: List[str] = Query(None)
+ lnbits_disabled_ext: List[str] = Query(None)
lnbits_funding_source: str = Query(None)
lnbits_force_https: bool = Query(None)
lnbits_reserve_fee_min: int = Query(None, ge=0)
@@ -23,4 +23,35 @@ class UpdateSettings(BaseModel):
lnbits_denomination: str = Query(None)
lnbits_theme: str = Query(None)
lnbits_custom_logo: str = Query(None)
- lnbits_ad_space: List[str] = Query(None)
+ lnbits_ad_space: List[str] = Query(None)
+
+ # funding sources
+ fake_wallet_secret: str = Query(None)
+ lnbits_endpoint: str = Query(None)
+ lnbits_key: str = Query(None)
+ cliche_endpoint: str = Query(None)
+ corelightning_rpc: str = Query(None)
+ eclair_url: str = Query(None)
+ eclair_pass: str = Query(None)
+ lnd_rest_endpoint: str = Query(None)
+ lnd_rest_cert: str = Query(None)
+ lnd_rest_macaroon: str = Query(None)
+ lnd_rest_macaroon_encrypted: str = Query(None)
+ lnd_cert: str = Query(None)
+ lnd_admin_macaroon: str = Query(None)
+ lnd_invoice_macaroon: str = Query(None)
+ lnd_grpc_endpoint: str = Query(None)
+ lnd_grpc_cert: str = Query(None)
+ lnd_grpc_port: int = Query(None, ge=0)
+ lnd_grpc_admin_macaroon: str = Query(None)
+ lnd_grpc_invoice_macaroon: str = Query(None)
+ lnd_grpc_macaroon_encrypted: str = Query(None)
+ lnpay_api_endpoint: str = Query(None)
+ lnpay_api_key: str = Query(None)
+ lnpay_wallet_key: str = Query(None)
+ lntxbot_api_endpoint: str = Query(None)
+ lntxbot_key: str = Query(None)
+ opennode_api_endpoint: str = Query(None)
+ opennode_key: str = Query(None)
+ spark_url: str = Query(None)
+ spark_token: str = Query(None)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index 8b5456f1..a523d4e5 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -8,9 +8,7 @@
Funding Source Info
{%raw%}
-
- Funding Source: {{settings.lnbits_backend_wallet_class}}
-
+ Funding Source: {{settings.lnbits_backend_wallet_class}}
Balance: {{balance / 1000}} sats
{%endraw%}
@@ -60,21 +58,30 @@
- Funding Sources
+ Funding Sources (Requires server restart)
-
+
-
-
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index d8111595..ccaddda4 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -3,7 +3,13 @@
-
+
u !== user
- )
+ this.settings.lnbits_admin_users = admin_users.filter(u => u !== user)
},
addAllowedUser() {
let addUser = this.formData.allowed_users_add
@@ -155,7 +335,9 @@
},
removeAllowedUser(user) {
let allowed_users = this.settings.lnbits_allowed_users
- this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user)
+ this.settings.lnbits_allowed_users = allowed_users.filter(
+ u => u !== user
+ )
},
addAdSpace() {
let adSpace = this.formData.ad_space_add
@@ -208,8 +390,19 @@
LNbits.utils.notifyApiError(error)
})
},
+ updateFundingData(){
+ this.settings.lnbits_allowed_funding_sources.map(f => {
+ let opts = this.funding_sources.get(f)
+ if (!opts) return
+
+ Object.keys(opts).forEach(e => {
+ opts[e].value = this.settings[e]
+ })
+ })
+ console.log("funding", this.funding_sources)
+ },
updateSettings() {
- let data = {
+ let data = {
...this.formData
}
LNbits.api
@@ -222,11 +415,13 @@
.then(response => {
this.settings = response.data.settings
this.formData = _.clone(this.settings)
+ this.updateFundingData()
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
icon: null
})
+ console.log(this.settings)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -262,7 +457,7 @@
LNbits.utils.notifyApiError(error)
})
}
- },
+ }
})
{% endblock %}
From 761fc427defc590913124f1ad2f1b518633fbad4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 10 Oct 2022 23:27:46 +0200
Subject: [PATCH 0284/1058] add callback for saas app
---
lnbits/app.py | 2 +-
lnbits/extensions/admin/migrations.py | 3 +++
lnbits/extensions/admin/views_api.py | 5 -----
lnbits/settings.py | 32 +++++++++++++++++++++++----
4 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 50f218b7..49ad8d77 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -64,7 +64,7 @@ def create_app() -> FastAPI:
# TODO: why those 2?
g().config = settings
- # g().base_url = f"http://{settings.host}:{settings.port}"
+ g().base_url = f"http://{settings.host}:{settings.port}"
app.add_middleware(GZipMiddleware, minimum_size=1000)
diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py
index c4bc98d8..ea698c27 100644
--- a/lnbits/extensions/admin/migrations.py
+++ b/lnbits/extensions/admin/migrations.py
@@ -6,6 +6,9 @@ async def m001_create_admin_settings_table(db):
debug TEXT,
host TEXT,
port INTEGER,
+ lnbits_saas_instance_id TEXT,
+ lnbits_saas_callback TEXT,
+ lnbits_saas_secret TEXT,
lnbits_path TEXT,
lnbits_commit TEXT,
lnbits_admin_users TEXT,
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 19b52e35..ae2959bc 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -53,8 +53,3 @@ async def api_delete_settings(
):
await delete_settings()
return {"status": "Success"}
-
-
-@admin_ext.get("/api/v1/backup/", status_code=HTTPStatus.OK)
-async def api_backup(user: User = Depends(check_admin)):
- return {"status": "not implemented"}
diff --git a/lnbits/settings.py b/lnbits/settings.py
index ffcdcc0a..f183211c 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,6 +1,7 @@
import importlib
import json
import subprocess
+import httpx
from os import path
from sqlite3 import Row
from typing import List, Optional
@@ -9,6 +10,7 @@ from loguru import logger
from pydantic import BaseSettings, Field, validator
+
def list_parse_fallback(v):
try:
return json.loads(v)
@@ -34,6 +36,11 @@ class Settings(BaseSettings):
lnbits_path: str = Field(default=".")
lnbits_commit: str = Field(default="unknown")
+ # saas
+ lnbits_saas_callback: Optional[str] = Field(default=None)
+ lnbits_saas_secret: Optional[str] = Field(default=None)
+ lnbits_saas_instance_id: Optional[str] = Field(default=None)
+
# users
lnbits_admin_users: List[str] = Field(default=[])
lnbits_allowed_users: List[str] = Field(default=[])
@@ -230,11 +237,28 @@ async def check_admin_settings():
http = "https" if settings.lnbits_force_https else "http"
user = settings.lnbits_admin_users[0]
- logger.warning(
- f" ✔️ Access admin user account at: {http}://{settings.host}:{settings.port}/wallet?usr={user}"
- )
+
+ admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ logger.warning(f"✔️ Access admin user account at: {admin_url}")
+
+ if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id:
+ with httpx.Client() as client:
+ headers = {
+ "Content-Type": "application/json; charset=utf-8",
+ "X-API-KEY": settings.lnbits_saas_secret
+ }
+ payload = {
+ "instance_id": settings.lnbits_saas_instance_id,
+ "adminuser": user
+ }
+ try:
+ r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload)
+ logger.warning("sent admin user to saas application")
+ except:
+ logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}")
+
except:
- logger.warning("admin.settings tables does not exist.")
+ logger.error("admin.settings tables does not exist.")
raise
From 620fd2569655aa0f45b486201bcac75654f28326 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Wed, 12 Oct 2022 13:08:59 +0200
Subject: [PATCH 0285/1058] bugfixes and fix topup wallet
---
.../admin/templates/admin/index.html | 6 ++--
lnbits/extensions/admin/views_api.py | 36 ++++++++++---------
lnbits/server.py | 1 +
lnbits/settings.py | 29 ++++++++++-----
lnbits/wallets/fake.py | 2 +-
5 files changed, 44 insertions(+), 30 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index ccaddda4..575b377f 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -369,10 +369,10 @@
topupWallet() {
LNbits.api
.request(
- 'POST',
+ 'PUT',
'/admin/api/v1/topup/?usr=' + this.g.user.id,
- this.wallet.id,
- this.wallet.amount
+ this.g.user.wallets[0].adminkey,
+ this.wallet
)
.then(response => {
this.$q.notify({
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index ae2959bc..63ed5b3c 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -1,7 +1,6 @@
from http import HTTPStatus
-from fastapi import Body, Depends, Request
-from loguru import logger
+from fastapi import Body, Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_wallet
@@ -9,47 +8,50 @@ from lnbits.core.models import User
from lnbits.decorators import check_admin
from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import UpdateSettings
-from lnbits.requestvars import g
from lnbits.server import server_restart
-from lnbits.settings import settings
from .crud import delete_settings, update_settings, update_wallet_balance
-@admin_ext.get("/api/v1/restart/", status_code=HTTPStatus.OK)
-async def api_restart_server(user: User = Depends(check_admin)):
+@admin_ext.get(
+ "/api/v1/restart/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
+async def api_restart_server() -> dict[str, str]:
server_restart.set()
return {"status": "Success"}
-@admin_ext.put("/api/v1/topup/", status_code=HTTPStatus.OK)
+@admin_ext.put(
+ "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
async def api_update_balance(
- wallet_id, topup_amount: int, user: User = Depends(check_admin)
-):
+ id: str = Body(...), amount: int = Body(...)
+) -> dict[str, str]:
try:
- wallet = await get_wallet(wallet_id)
+ await get_wallet(id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist."
)
- await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount))
+ await update_wallet_balance(wallet_id=id, amount=int(amount))
return {"status": "Success"}
-@admin_ext.put("/api/v1/settings/", status_code=HTTPStatus.OK)
+@admin_ext.put(
+ "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
async def api_update_settings(
- user: User = Depends(check_admin),
data: UpdateSettings = Body(...),
):
settings = await update_settings(data)
return {"status": "Success", "settings": settings.dict()}
-@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK)
-async def api_delete_settings(
- user: User = Depends(check_admin),
-):
+@admin_ext.delete(
+ "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
+)
+async def api_delete_settings() -> dict[str, str]:
await delete_settings()
return {"status": "Success"}
diff --git a/lnbits/server.py b/lnbits/server.py
index 79af8112..6d4cd2e7 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -52,6 +52,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
port=port,
host=host,
reload=reload,
+ forwarded_allow_ips="*",
ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile,
**d
diff --git a/lnbits/settings.py b/lnbits/settings.py
index f183211c..61dbd6f2 100644
--- a/lnbits/settings.py
+++ b/lnbits/settings.py
@@ -1,20 +1,19 @@
import importlib
import json
import subprocess
-import httpx
from os import path
from sqlite3 import Row
from typing import List, Optional
+import httpx
from loguru import logger
from pydantic import BaseSettings, Field, validator
-
def list_parse_fallback(v):
try:
return json.loads(v)
- except Exception as e:
+ except Exception:
replaced = v.replace(" ", "")
if replaced:
return replaced.split(",")
@@ -238,24 +237,36 @@ async def check_admin_settings():
http = "https" if settings.lnbits_force_https else "http"
user = settings.lnbits_admin_users[0]
- admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ admin_url = (
+ f"{http}://{settings.host}:{settings.port}/wallet?usr={user}"
+ )
logger.warning(f"✔️ Access admin user account at: {admin_url}")
- if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id:
+ if (
+ settings.lnbits_saas_callback
+ and settings.lnbits_saas_secret
+ and settings.lnbits_saas_instance_id
+ ):
with httpx.Client() as client:
headers = {
"Content-Type": "application/json; charset=utf-8",
- "X-API-KEY": settings.lnbits_saas_secret
+ "X-API-KEY": settings.lnbits_saas_secret,
}
payload = {
"instance_id": settings.lnbits_saas_instance_id,
- "adminuser": user
+ "adminuser": user,
}
try:
- r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload)
+ client.post(
+ settings.lnbits_saas_callback,
+ headers=headers,
+ json=payload,
+ )
logger.warning("sent admin user to saas application")
except:
- logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}")
+ logger.error(
+ f"error sending admin user to saas: {settings.lnbits_saas_callback}"
+ )
except:
logger.error("admin.settings tables does not exist.")
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 73458e8c..94ff5f48 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -19,7 +19,6 @@ from .base import (
class FakeWallet(Wallet):
- queue: asyncio.Queue = asyncio.Queue(0)
secret: str = settings.fake_wallet_secret
privkey: str = hashlib.pbkdf2_hmac(
"sha256",
@@ -98,6 +97,7 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value: Invoice = await self.queue.get()
yield value.payment_hash
From 9dbcd89c6ebdba4db6c0bc7d70d570a8ee5fffaa Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 13 Oct 2022 15:52:02 +0100
Subject: [PATCH 0286/1058] added tooltips, moved reset, and warnings
---
.../admin/templates/admin/index.html | 97 ++++++++++++-------
1 file changed, 61 insertions(+), 36 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 575b377f..7d268301 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -1,8 +1,14 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
-
-
+
+
+ Save your changes
-
-
-
+
+
+ Restart the server for changes to take effect
+
+
+
+
+ Add funds to a wallet.
+
+ > -->
+
+ Delete all settings and reset to defaults.
+
@@ -121,6 +136,7 @@
show: false
},
tab: 'funding',
+ needsRestart: false,
funding_sources: new Map([
['VoidWallet', null],
[
@@ -302,13 +318,12 @@
this.balance = +'{{ balance|safe }}'
this.formData = _.clone(this.settings) //model
this.updateFundingData()
-
console.log(this.settings)
},
computed: {
checkChanges() {
return !_.isEqual(this.settings, this.formData)
- },
+ }
},
methods: {
addAdminUser() {
@@ -361,6 +376,7 @@
message: 'Success! Restarted Server',
icon: null
})
+ this.needsRestart = false
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -390,16 +406,15 @@
LNbits.utils.notifyApiError(error)
})
},
- updateFundingData(){
+ updateFundingData() {
this.settings.lnbits_allowed_funding_sources.map(f => {
let opts = this.funding_sources.get(f)
if (!opts) return
-
+
Object.keys(opts).forEach(e => {
opts[e].value = this.settings[e]
})
})
- console.log("funding", this.funding_sources)
},
updateSettings() {
let data = {
@@ -415,31 +430,41 @@
.then(response => {
this.settings = response.data.settings
this.formData = _.clone(this.settings)
+ this.needsRestart = true
this.updateFundingData()
this.$q.notify({
type: 'positive',
message: 'Success! Settings changed!',
icon: null
})
- console.log(this.settings)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteSettings() {
- LNbits.api
- .request('DELETE', '/admin/api/v1/settings/?usr=' + this.g.user.id)
- .then(response => {
- this.$q.notify({
- type: 'positive',
- message:
- 'Success! Restored settings to defaults, restart required!',
- icon: null
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
+ LNbits.utils
+ .confirmDialog(
+ 'Are you sure you want to restore settings to default?'
+ )
+ .onOk(() => {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/admin/api/v1/settings/?usr=' + this.g.user.id
+ )
+ .then(response => {
+ this.$q.notify({
+ type: 'positive',
+ message:
+ 'Success! Restored settings to defaults, restart required!',
+ icon: null
+ })
+ this.needsRestart = true
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
})
},
downloadBackup() {
From 31a7a87774e5fcd87962440bc8c20510f607a328 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 13 Oct 2022 15:52:26 +0100
Subject: [PATCH 0287/1058] moved to columns
---
.../admin/templates/admin/_tab_funding.html | 54 +++++++++----------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html
index a523d4e5..a69ecb47 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_funding.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html
@@ -27,38 +27,38 @@
:options="settings.lnbits_allowed_funding_sources"
>
-
+
-
-
-
- Funding Sources (Requires server restart)
+
+ Funding Sources (Requires server restart)
+
Date: Thu, 13 Oct 2022 15:52:39 +0100
Subject: [PATCH 0288/1058] themes not displaying fixed
---
lnbits/extensions/admin/templates/admin/_tab_theme.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html
index 46bf83e9..c327733f 100644
--- a/lnbits/extensions/admin/templates/admin/_tab_theme.html
+++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html
@@ -63,7 +63,7 @@
Themes
Date: Fri, 21 Oct 2022 10:00:47 +0200
Subject: [PATCH 0289/1058] add loop to uvicorn
---
lnbits/server.py | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/lnbits/server.py b/lnbits/server.py
index 6d4cd2e7..eb7c12b1 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -1,12 +1,7 @@
-import asyncio
-
import uvloop
-
uvloop.install()
-import contextlib
import multiprocessing as mp
-import sys
import time
import click
@@ -49,6 +44,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
while True:
config = uvicorn.Config(
"lnbits.__main__:app",
+ loop="uvloop",
port=port,
host=host,
reload=reload,
@@ -65,9 +61,10 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload:
server_restart.clear()
server.should_exit = True
server.force_exit = True
+ time.sleep(3)
process.terminate()
process.join()
- time.sleep(3)
+ time.sleep(1)
server_restart = mp.Event()
From b14b9f3b3a48f5a2544864783de7129e8ae5bfb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 11:13:40 +0200
Subject: [PATCH 0290/1058] add get settings endpoint with only values you can
also save
---
lnbits/app.py | 6 +----
lnbits/extensions/admin/crud.py | 12 +++++++++-
lnbits/extensions/admin/models.py | 6 ++++-
.../admin/templates/admin/index.html | 22 +++++++++++++++----
lnbits/extensions/admin/views_api.py | 7 +++++-
lnbits/server.py | 1 +
6 files changed, 42 insertions(+), 12 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 49ad8d77..959a8168 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -62,10 +62,6 @@ def create_app() -> FastAPI:
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)
- # TODO: why those 2?
- g().config = settings
- g().base_url = f"http://{settings.host}:{settings.port}"
-
app.add_middleware(GZipMiddleware, minimum_size=1000)
register_startup(app)
@@ -174,7 +170,7 @@ def register_assets(app: FastAPI):
@app.on_event("startup")
async def vendored_assets_variable():
- if g().config.debug:
+ if settings.debug:
g().VENDORED_JS = map(url_for_vendored, get_js_vendored())
g().VENDORED_CSS = map(url_for_vendored, get_css_vendored())
else:
diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py
index cc937b5e..2ce91612 100644
--- a/lnbits/extensions/admin/crud.py
+++ b/lnbits/extensions/admin/crud.py
@@ -6,7 +6,7 @@ from lnbits.settings import Settings, read_only_variables
from lnbits.tasks import internal_invoice_queue
from . import db
-from .models import UpdateSettings
+from .models import AdminSettings, UpdateSettings
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
@@ -26,6 +26,16 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
await internal_invoice_queue.put(internal_id)
+async def get_settings() -> AdminSettings:
+ row = await db.fetchone("SELECT * FROM admin.settings")
+ all_settings = Settings(**row)
+ settings = AdminSettings()
+ for key, value in row.items():
+ if hasattr(settings, key):
+ setattr(settings, key, getattr(all_settings, key))
+ return settings
+
+
async def update_settings(data: UpdateSettings) -> Settings:
fields = []
for key, value in data.dict(exclude_none=True).items():
diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py
index d9d2b22f..31811659 100644
--- a/lnbits/extensions/admin/models.py
+++ b/lnbits/extensions/admin/models.py
@@ -1,4 +1,4 @@
-from typing import List
+from typing import List, Optional
from fastapi import Query
from pydantic import BaseModel
@@ -55,3 +55,7 @@ class UpdateSettings(BaseModel):
opennode_key: str = Query(None)
spark_url: str = Query(None)
spark_token: str = Query(None)
+
+
+class AdminSettings(UpdateSettings):
+ lnbits_allowed_funding_sources: Optional[List[str]]
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html
index 7d268301..10391261 100644
--- a/lnbits/extensions/admin/templates/admin/index.html
+++ b/lnbits/extensions/admin/templates/admin/index.html
@@ -314,11 +314,8 @@
}
},
created: function () {
- this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data
+ this.getSettings()
this.balance = +'{{ balance|safe }}'
- this.formData = _.clone(this.settings) //model
- this.updateFundingData()
- console.log(this.settings)
},
computed: {
checkChanges() {
@@ -416,6 +413,23 @@
})
})
},
+ getSettings() {
+ LNbits.api
+ .request(
+ 'GET',
+ '/admin/api/v1/settings/?usr=' + this.g.user.id,
+ this.g.user.wallets[0].adminkey
+ )
+ .then(response => {
+ this.settings = response.data
+ this.formData = _.clone(this.settings)
+ this.updateFundingData()
+ console.log(this.settings)
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
updateSettings() {
let data = {
...this.formData
diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py
index 63ed5b3c..57d62ed4 100644
--- a/lnbits/extensions/admin/views_api.py
+++ b/lnbits/extensions/admin/views_api.py
@@ -10,7 +10,7 @@ from lnbits.extensions.admin import admin_ext
from lnbits.extensions.admin.models import UpdateSettings
from lnbits.server import server_restart
-from .crud import delete_settings, update_settings, update_wallet_balance
+from .crud import delete_settings, get_settings, update_settings, update_wallet_balance
@admin_ext.get(
@@ -21,6 +21,11 @@ async def api_restart_server() -> dict[str, str]:
return {"status": "Success"}
+@admin_ext.get("/api/v1/settings/", dependencies=[Depends(check_admin)])
+async def api_get_settings() -> UpdateSettings:
+ return await get_settings()
+
+
@admin_ext.put(
"/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
diff --git a/lnbits/server.py b/lnbits/server.py
index eb7c12b1..ecf7ff62 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -1,4 +1,5 @@
import uvloop
+
uvloop.install()
import multiprocessing as mp
From 8fbf10909961c13b6c39008ae4ce3aaa750b9a51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 25 Oct 2022 09:25:37 +0200
Subject: [PATCH 0291/1058] fix poetry lockCCC
---
poetry.lock | 24 +-----------------------
1 file changed, 1 insertion(+), 23 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 45968390..bbce3c5c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -258,24 +258,6 @@ category = "main"
optional = false
python-versions = "*"
-[[package]]
-name = "environs"
-version = "9.3.3"
-description = "simplified environment variable parsing"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-marshmallow = ">=3.0.0"
-python-dotenv = "*"
-
-[package.extras]
-dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
-django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
-tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
-
[[package]]
name = "fastapi"
version = "0.78.0"
@@ -1051,7 +1033,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
+content-hash = "e798b36b5941b43ee249bc196fcfb28d8ee712947336d21467651c672ba0106b"
[metadata.files]
aiofiles = [
@@ -1307,10 +1289,6 @@ enum34 = [
{file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"},
{file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"},
]
-environs = [
- {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
- {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
-]
fastapi = [
{file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
{file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
From ecd5dadcd13ed8e92bc505c9ee8064886e40d732 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 26 Oct 2022 02:30:28 +0200
Subject: [PATCH 0292/1058] doesnt work yet
---
.../cashu/templates/cashu/wallet.html | 390 ++++++++++--------
1 file changed, 218 insertions(+), 172 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 11ac044b..ee243c1f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -71,7 +71,7 @@ page_container %}
-
+
@@ -109,7 +109,7 @@ page_container %}
{% endraw %}
-
+
- How much would you like to buy?
+ Create a Lightning invoice
@@ -260,7 +261,7 @@ page_container %}
color="grey"
>Copy invoice
-
Request Invoice
Show Tokens Send Tokens
Burn Tokens
-
Accept Tokens Receive Tokens
Close
-
+
{% raw %}
- Amount: {{ sellData.invoice.sat }}
+ Amount: {{ payInvoiceData.invoice.sat }}
sats
- Description: {{ sellData.invoice.description }}
- Expire date: {{ sellData.invoice.expireDate }}
- Expired: {{ sellData.invoice.expired }}
- Hash: {{ sellData.invoice.hash }} {% endraw %}
+ Description: {{ payInvoiceData.invoice.description
+ }}
+ Expire date: {{ payInvoiceData.invoice.expireDate
+ }}
+ Expired: {{ payInvoiceData.invoice.expired }}
+ Hash: {{ payInvoiceData.invoice.hash }} {% endraw
+ %}
Check Invoice
-
Sell Token
+
Pay invoice
Close
@@ -458,7 +460,7 @@ page_container %}
bolt11: '',
hash: ''
},
- sellData: {
+ payInvoiceData: {
invoice: '',
bolt11: ''
},
@@ -475,6 +477,7 @@ page_container %}
showPayInvoice: false,
showSendTokens: false,
showReceiveTokens: false,
+ promises: [],
tokens: [],
tab: 'tokens',
@@ -614,7 +617,7 @@ page_container %}
},
tokenList: function () {
- const x = this.tokens
+ const x = this.proofs
.filter(t => t.promises?.length)
.map(t => t.blindedMessages)
.flat()
@@ -636,7 +639,7 @@ page_container %}
},
balance: function () {
- return this.tokens
+ return this.proofs
.filter(t => t.promises?.length)
.map(t => t.blindedMessages)
.flat()
@@ -891,8 +894,8 @@ page_container %}
showPayInvoiceDialog: function () {
console.log('### showPayInvoiceDialog')
- this.sellData.invoice = ''
- this.sellData.bolt11 = ''
+ this.payInvoiceData.invoice = ''
+ this.payInvoiceData.bolt11 = ''
this.showPayInvoice = true
},
@@ -909,31 +912,6 @@ page_container %}
this.showReceiveTokens = true
},
- requestInvoice: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}`
- )
- console.log('### data', data)
-
- this.invoiceData.bolt11 = data.pr
- this.invoiceData.hash = data.hash
- this.invoicesCashu.push({
- ..._.clone(this.invoiceData),
- date: currentDateStr(),
- status: 'pending'
- })
- this.storeinvoicesCashu()
- const amounts = splitAmount(this.invoiceData.amount)
- await this.requestTokens(amounts, this.invoiceData.hash)
- this.tab = 'orders'
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- }
- },
-
checkXXXXXX: async function () {
for (const invoice of this.invoicesCashu) {
if (invoice.status === 'pending') {
@@ -955,24 +933,72 @@ page_container %}
}
},
- recheckInvoice: async function (hash) {
- console.log('### recheckInvoice.hash', hash)
- const tokens = this.tokens.find(bt => bt.hash === hash)
- console.log('### recheckInvoice.tokens', tokens)
- if (!tokens) {
- console.error('####### no token for hash', hash)
- return
- }
- const promises = await this.fetchPromisesFromMint(
- hash,
- tokens.blindedMessages
- )
- if (promises && promises.length) {
- tokens.promises = promises
- tokens.status = 'paid'
- this.storeTokens()
+ //////////////////////// MINT //////////////////////////////////////////
- const invoice = this.invoicesCashu.find(bo => bo.hash === hash)
+ requestMint: async function () {
+ // gets an invoice from the mint to get new tokens
+ try {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}`
+ )
+ console.log('### data', data)
+
+ this.invoiceData.bolt11 = data.pr
+ this.invoiceData.hash = data.hash
+ this.invoicesCashu.push({
+ ..._.clone(this.invoiceData),
+ date: currentDateStr(),
+ status: 'pending'
+ })
+ this.storeinvoicesCashu()
+ this.tab = 'invoices'
+ return data
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
+ },
+ mintApi: async function (amounts, payment_hash) {
+ console.log('### promises', payment_hash)
+ try {
+ let secrets = generateSecrets(amounts)
+ let {blinded_messages, rs} = constructOutputs(amounts, secrets)
+ const {promises} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/mint?payment_hash=${payment_hash}`,
+ '',
+ {
+ blinded_messages: blinded_messages
+ }
+ )
+ console.log('### promises data', promises)
+ return promises
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
+ },
+ mint: async function (amount, payment_hash) {
+ try {
+ const split = splitAmount(amount)
+ const proofs = await mintApi(split, payment_hash)
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ }
+ },
+ recheckInvoice: async function (payment_hash) {
+ console.log('### recheckInvoice.hash', payment_hash)
+ const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
+ const amounts = splitAmount(invoice.amount)
+ const newTokens = await this.buildTokens(amounts, payment_hash)
+ const promises = await this.mint(invoice.amount, payment_hash)
+ if (promises && promises.length) {
+ newTokens.promises = promises
+ newTokens.status = 'paid'
+ this.proofs.push(newTokens)
+ this.storeProofs()
invoice.status = 'paid'
this.storeinvoicesCashu()
}
@@ -980,56 +1006,116 @@ page_container %}
requestTokens: async function (amounts, paymentHash) {
const newTokens = await this.buildTokens(amounts, paymentHash)
- this.tokens.push(newTokens)
- this.storeTokens()
- console.log('### this.tokens', this.tokens)
- // await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens)
+ // this.proofs.push(newTokens)
+ // this.storeProofs()
+ // console.log('### this.proofs', this.proofs)
+ // await this.mint(paymentHash, newTokens.newTokens)
},
- fetchPromisesFromMint: async function (hash, blindedMessages) {
- console.log('### promises', hash, blindedMessages)
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/mint?payment_hash=${hash}`,
- '',
- {
- blinded_messages: blindedMessages
- }
- )
- console.log('### promises data', data)
- return data
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
+ generateSecrets: async function (amounts) {
+ const secrets = []
+ for (let i = 0; i < amounts.length; i++) {
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ secrets.push(encodedSecret)
+ }
+ return secrets
+ },
+ constructOutputs: async function (amounts, secrets) {
+ const blindedMessages = []
+ const randomBlindingFactors = []
+ for (let i = 0; i < amounts.length; i++) {
+ const {B_, r} = await step1Bob(secret)
+ blindedMessages.push(B_)
+ randomBlindingFactors.push(r)
+ }
+ return {
+ blindedMessages,
+ randomBlindingFactors
}
},
- buildAndShowTokens: async function () {
+ constructProofs: function (promises, secrets, rs) {
+ const proofs = []
+ for (let i = 0; i < promises.length; i++) {
+ let {amount, C, secret} = promiseToProof(
+ promises[i].amount,
+ promises[i]['C_'],
+ promises[i].secret,
+ promises[i].randomBlindingFactor
+ )
+ }
+ return proofs
+ },
+
+ promiseToProof: function (amount, C_hex, secret, randomBlindingFactor) {
+ const C_ = nobleSecp256k1.Point.fromHex(C_hex)
+ const A = this.keys[amount]
+ const C = step3Bob(
+ C_,
+ randomBlindingFactor,
+ nobleSecp256k1.Point.fromHex(A)
+ )
+ return {
+ amount,
+ C: C.toHex(true),
+ secret
+ }
+ },
+ buildTokens: async function (amounts, paymentHash) {
+ const blindedMessages = []
+ const secrets = []
+ const randomBlindingFactors = []
+ for (let i = 0; i < amounts.length; i++) {
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
+ // todo: base64Url
+ const encodedSecret = uint8ToBase64.encode(secret)
+ secrets.push(encodedSecret)
+ const {B_, randomBlindingFactor} = await step1Bob(secret)
+ randomBlindingFactors.push(randomBlindingFactor)
+ blindedMessages.push({amount: amounts[i], B_: B_})
+ }
+
+ const newTokens = {
+ hash: paymentHash,
+ blindedMessages,
+ randomBlindingFactors,
+ secrets,
+ status: 'pending'
+ }
+ return newTokens
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////////
+
+ sendTokens: async function () {
const amounts = splitAmount(this.sendData.amount)
const sendTokens = []
- for (const amount of amounts) {
- const token = this.findTokenForAmount(amount)
- if (token) {
- sendTokens.push(token)
- } else {
- this.$q.notify({
- timeout: 5000,
- type: 'warning',
- message: `Cannot select amount for denomination ${amount}`
- })
- this.sendData.tokens = ''
- this.sendData.tokensBase64 = ''
- return
- }
- }
+ sendTokens.push(this.proofs)
+ // for (const amount of amounts) {
+ // const token = this.findTokenForAmount(amount)
+ // if (token) {
+ // sendTokens.push(token)
+ // } else {
+ // this.$q.notify({
+ // timeout: 5000,
+ // type: 'warning',
+ // message: `Cannot select amount for denomination ${amount}`
+ // })
+ // this.sendData.tokens = ''
+ // this.sendData.tokensBase64 = ''
+ // return
+ // }
+ // }
+ this.sendData.tokens = ''
+ this.sendData.tokensBase64 = ''
console.log('### sendTokens', sendTokens)
- this.sendData.tokens = sendTokens.map(t => {
+ this.sendData.tokens = sendTokens.map((token, tokenIndex) => {
return this.promiseToProof(
- t.promise.amount,
- t.promise['C_'],
- t.secret,
- t.randomBlindingFactor
+ token.promises[tokenIndex].amount,
+ token.promises[tokenIndex]['C_'],
+ token.promises[tokenIndex].secret,
+ token.promises[tokenIndex].randomBlindingFactor
)
})
console.log('### this.sendData.tokens', this.sendData.tokens)
@@ -1038,7 +1124,7 @@ page_container %}
burnTokens: function () {
for (const sentToken of this.sendData.tokens) {
- for (const token of this.tokens) {
+ for (const token of this.proofs) {
if (token.status === 'paid') {
const secretIndex = token.secrets.findIndex(
s => s === sentToken.secret
@@ -1058,12 +1144,12 @@ page_container %}
timeout: 5000,
message: 'Tokens burned'
})
- this.storeTokens()
+ this.storeProofs()
this.showSendTokens = false
- console.log('### this.tokens', this.tokens)
+ console.log('### this.proofs', this.proofs)
},
- acceptTokens: async function () {
+ receiveTokens: async function () {
this.showReceiveTokens = false
console.log('### receive tokens', this.receiveData.tokensBase64)
if (this.receiveData.tokensBase64) {
@@ -1097,8 +1183,8 @@ page_container %}
// Object.assign(newTokens[i], promises)
// }
console.log('newTokens 2', newTokens)
- this.tokens.push(newTokens)
- this.storeTokens()
+ this.proofs.push(newTokens)
+ this.storeProofs()
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
@@ -1107,7 +1193,7 @@ page_container %}
},
findTokenForAmount: function (amount) {
- for (const token of this.tokens) {
+ for (const token of this.proofs) {
const index = token.promises?.findIndex(p => p.amount === amount)
if (index >= 0) {
return {
@@ -1119,37 +1205,10 @@ page_container %}
}
},
- constructProof: function (token) {},
-
- buildTokens: async function (amounts, paymentHash) {
- const blindedMessages = []
- const secrets = []
- const randomBlindingFactors = []
- for (let i = 0; i < amounts.length; i++) {
- const secret = nobleSecp256k1.utils.randomBytes(32)
- // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
- // todo: base64Url
- const encodedSecret = uint8ToBase64.encode(secret)
- secrets.push(encodedSecret)
- const {B_, randomBlindingFactor} = await step1Bob(secret)
- randomBlindingFactors.push(randomBlindingFactor)
- blindedMessages.push({amount: amounts[i], B_: B_})
- }
-
- const newTokens = {
- hash: paymentHash,
- blindedMessages,
- randomBlindingFactors,
- secrets,
- status: 'pending'
- }
- return newTokens
- },
-
checkInvoice: function () {
console.log('#### checkInvoice')
try {
- const invoice = decode(this.sellData.bolt11)
+ const invoice = decode(this.payInvoiceData.bolt11)
const cleanInvoice = {
msat: invoice.human_readable_part.amount,
@@ -1177,10 +1236,13 @@ page_container %}
}
}
- this.sellData.invoice = cleanInvoice
+ this.payInvoiceData.invoice = cleanInvoice
})
- console.log('#### this.sellData.invoice', this.sellData.invoice)
+ console.log(
+ '#### this.payInvoiceData.invoice',
+ this.payInvoiceData.invoice
+ )
} catch (error) {
this.$q.notify({
timeout: 5000,
@@ -1191,10 +1253,10 @@ page_container %}
}
},
- sellTokens: async function () {
+ melt: async function () {
console.log('#### sell tokens')
- const amount = this.sellData.invoice.sat
- const paidTokens = this.tokens.filter(t => t.promises?.length)
+ const amount = this.payInvoiceData.invoice.sat
+ const paidTokens = this.proofs.filter(t => t.promises?.length)
console.log('### paidTokens', paidTokens)
const proofs = paidTokens.map(token => {
return token.promises.map((promise, promiseIndex) => {
@@ -1215,7 +1277,7 @@ page_container %}
const payload = {
proofs: proofs.flat(),
amount,
- invoice: this.sellData.bolt11
+ invoice: this.payInvoiceData.bolt11
}
console.log('#### payload', JSON.stringify(payload))
try {
@@ -1237,22 +1299,6 @@ page_container %}
// C_hex = promise['C_']
// amount = promise.amount
- promiseToProof: function (amount, C_hex, secret, randomBlindingFactor) {
- const C_ = nobleSecp256k1.Point.fromHex(C_hex)
- const A = this.keys[amount]
-
- const C = step3Bob(
- C_,
- randomBlindingFactor,
- nobleSecp256k1.Point.fromHex(A)
- )
-
- return {
- amount,
- secret,
- C: C.toHex(true)
- }
- },
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
@@ -1269,10 +1315,10 @@ page_container %}
JSON.stringify(this.invoicesCashu)
)
},
- storeTokens: function () {
+ storeProofs: function () {
localStorage.setItem(
- 'cashu.tokens',
- JSON.stringify(this.tokens, bigIntStringify)
+ 'cashu.proofs',
+ JSON.stringify(this.proofs, bigIntStringify)
)
}
},
@@ -1330,9 +1376,9 @@ page_container %}
this.invoicesCashu = JSON.parse(
localStorage.getItem('cashu.invoicesCashu') || '[]'
)
- this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
+ this.proofs = JSON.parse(localStorage.getItem('cashu.proofs') || '[]')
console.log('### invoicesCashu', this.invoicesCashu)
- console.table('### tokens', this.tokens)
+ console.table('### tokens', this.proofs)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
From 970748216f0108c43dc2285cd9a85fe75106c2d3 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 28 Oct 2022 19:34:04 +0200
Subject: [PATCH 0293/1058] split doesnt work yet
---
lnbits/extensions/cashu/static/js/dhke.js | 12 +-
.../cashu/templates/cashu/wallet.html | 417 +++++++++++-------
lnbits/extensions/cashu/views_api.py | 1 -
3 files changed, 269 insertions(+), 161 deletions(-)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index c9e2d146..c35a34e9 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -19,17 +19,15 @@ async function hashToCurve(secretMessage) {
return point
}
-async function step1Bob(secretMessage) {
+async function step1Alice(secretMessage) {
const Y = await hashToCurve(secretMessage)
- const randomBlindingFactor = bytesToNumber(
- nobleSecp256k1.utils.randomPrivateKey()
- )
- const P = nobleSecp256k1.Point.fromPrivateKey(randomBlindingFactor)
+ const r = bytesToNumber(nobleSecp256k1.utils.randomPrivateKey())
+ const P = nobleSecp256k1.Point.fromPrivateKey(r)
const B_ = Y.add(P)
- return {B_: B_.toHex(true), randomBlindingFactor}
+ return {B_: B_.toHex(true), r}
}
-function step3Bob(C_, r, A) {
+function step3Alice(C_, r, A) {
const rInt = BigInt(r)
const C = C_.subtract(A.multiply(rInt))
return C
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index ee243c1f..4af9c148 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -426,6 +426,10 @@ page_container %}
}
{% endblock %} {% block scripts %}
+
+
+
+
-
-
-
-
{% endblock %}
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index d4654c55..4d3e854c 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -347,7 +347,6 @@ async def split(
proofs = payload.proofs
amount = payload.amount
outputs = payload.outputs.blinded_messages
- # backwards compatibility with clients < v0.2.2
assert outputs, Exception("no outputs provided.")
split_return = None
try:
From ff724bf744db8fa16bee39b5cff11be9fd8132b2 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 2 Nov 2022 23:43:37 +0100
Subject: [PATCH 0294/1058] for r, dont use bigint but hex string
---
lnbits/extensions/cashu/static/js/dhke.js | 8 +++++---
lnbits/extensions/cashu/templates/cashu/wallet.html | 10 ++++++++--
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index c35a34e9..935bf6d4 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -21,14 +21,16 @@ async function hashToCurve(secretMessage) {
async function step1Alice(secretMessage) {
const Y = await hashToCurve(secretMessage)
- const r = bytesToNumber(nobleSecp256k1.utils.randomPrivateKey())
+ const rpk = nobleSecp256k1.utils.randomPrivateKey()
+ const r = bytesToNumber(rpk)
const P = nobleSecp256k1.Point.fromPrivateKey(r)
const B_ = Y.add(P)
- return {B_: B_.toHex(true), r}
+ return {B_: B_.toHex(true), r: nobleSecp256k1.utils.bytesToHex(rpk)}
}
function step3Alice(C_, r, A) {
- const rInt = BigInt(r)
+ // const rInt = BigInt(r)
+ const rInt = bytesToNumber(r)
const C = C_.subtract(A.multiply(rInt))
return C
}
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 4af9c148..105c1e08 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1060,7 +1060,11 @@ page_container %}
promiseToProof: function (id, amount, C_hex, secret, r) {
const C_ = nobleSecp256k1.Point.fromHex(C_hex)
const A = this.keys[amount]
- const C = step3Alice(C_, r, nobleSecp256k1.Point.fromHex(A))
+ const C = step3Alice(
+ C_,
+ nobleSecp256k1.utils.hexToBytes(r),
+ nobleSecp256k1.Point.fromHex(A)
+ )
return {
id,
amount,
@@ -1226,7 +1230,9 @@ page_container %}
const rs = []
for (let i = 0; i < amounts.length; i++) {
const secret = nobleSecp256k1.utils.randomBytes(32)
- // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
+ // const secret = nobleSecp256k1.utils.hexToBytes(
+ // '0000000000000000000000000000000000000000000000000000000000000000'
+ // )
// todo: base64Url
const encodedSecret = uint8ToBase64.encode(secret)
secrets.push(encodedSecret)
From dd36b9b30f98fa13bfe7e54f65a307254355978f Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Thu, 3 Nov 2022 14:20:38 +0200
Subject: [PATCH 0295/1058] fix: secret message format
---
lnbits/extensions/cashu/static/js/dhke.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index c9e2d146..7b3abd80 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -20,6 +20,8 @@ async function hashToCurve(secretMessage) {
}
async function step1Bob(secretMessage) {
+ secretMessage = nobleSecp256k1.utils.bytesToHex(secretMessage)
+ secretMessage = new TextEncoder().encode(secretMessage);
const Y = await hashToCurve(secretMessage)
const randomBlindingFactor = bytesToNumber(
nobleSecp256k1.utils.randomPrivateKey()
From bddb13269bf26dda4b4c9feb7ecb8542a2774bd9 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Thu, 3 Nov 2022 15:15:09 +0200
Subject: [PATCH 0296/1058] fix: token validation
---
lnbits/extensions/cashu/static/js/dhke.js | 3 +++
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
lnbits/extensions/cashu/views_api.py | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index 935bf6d4..9c7aee1b 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -20,6 +20,9 @@ async function hashToCurve(secretMessage) {
}
async function step1Alice(secretMessage) {
+ // todo: document & validate `secretMessage` format
+ secretMessage = uint8ToBase64.encode(secretMessage)
+ secretMessage = new TextEncoder().encode(secretMessage);
const Y = await hashToCurve(secretMessage)
const rpk = nobleSecp256k1.utils.randomPrivateKey()
const r = bytesToNumber(rpk)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 105c1e08..26bc3d26 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1278,7 +1278,7 @@ page_container %}
// }
this.sendData.tokens = ''
this.sendData.tokensBase64 = ''
- console.log('### sendTokens', sendTokens)
+ // console.log('### sendTokens', sendTokens)
// this.sendData.tokens = sendTokens.map((token, tokenIndex) => {
// return this.promiseToProof(
// token.promises[tokenIndex].amount,
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 4d3e854c..806347cb 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -353,7 +353,7 @@ async def split(
keyset = ledger.keysets.keysets[cashu.keyset_id]
split_return = await ledger.split(proofs, amount, outputs, keyset)
except Exception as exc:
- HTTPException(
+ raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(exc),
)
From 359b8bc4d2e14e93ec68a25dd9c1e3e39ac50627 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 15:25:51 +0100
Subject: [PATCH 0297/1058] works
---
.../cashu/templates/cashu/wallet.html | 579 ++++++++++--------
1 file changed, 335 insertions(+), 244 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 26bc3d26..3a9c48db 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -10,8 +10,9 @@ page_container %}
Pay invoice
@@ -43,28 +45,28 @@ page_container %}
-
+
Receive Receive Tokens
-
-
@@ -82,24 +84,24 @@ page_container %}
:data="tokenList"
:columns="tokensTable.columns"
:pagination.sync="tokensTable.pagination"
- no-data-label="No tokens made yet"
+ no-data-label="No tokens yet"
:filter="tokensTable.filter"
>
{% raw %}
- {{props.row.denomination}}
+ {{props.row.value}}
{{props.row.count}}
-
- {{props.row.value}}
+
+ {{props.row.sum}}
{{props.row.memo}}
@@ -181,14 +183,14 @@ page_container %}
indicator-color="transparent"
>
-
-
-
+
+
@@ -227,13 +229,12 @@ page_container %}
dense
v-model.number="invoiceData.amount"
label="Amount ({{LNBITS_DENOMINATION}}) *"
- mask="#.##"
+ mask="#"
fill-mask="0"
reverse-fill-mask
- type="number"
+ autofocus
class="q-mb-lg"
>
-
Copy invoice
- Request Invoice
+ Recheck
+ -->
+ Create Invoice
Close
-
@@ -310,13 +322,22 @@ page_container %}
Send Tokens
- Burn Tokens -->
+ Copy token
-
Receive Tokens
@@ -394,7 +417,9 @@ page_container %}
color="grey"
>Check Invoice
-
Pay invoice
+
Pay invoice
Close
@@ -464,6 +489,7 @@ page_container %}
bolt11: '',
hash: ''
},
+ invoiceCheckListener: () => {},
payInvoiceData: {
invoice: '',
bolt11: ''
@@ -553,6 +579,8 @@ page_container %}
}
],
pagination: {
+ sortBy: 'date',
+ descending: true,
rowsPerPage: 10
},
filter: null
@@ -561,10 +589,10 @@ page_container %}
tokensTable: {
columns: [
{
- name: 'denomination',
+ name: 'value',
align: 'left',
- label: 'Denomination',
- field: 'denomination',
+ label: 'Value ({{LNBITS_DENOMINATION}})',
+ field: 'value',
sortable: true
},
{
@@ -575,19 +603,19 @@ page_container %}
sortable: true
},
{
- name: 'value',
+ name: 'sum',
align: 'left',
- label: 'Value',
- field: 'value',
- sortable: true
- },
- {
- name: 'memo',
- align: 'left',
- label: 'Memo',
- field: 'memo',
+ label: 'Sum ({{LNBITS_DENOMINATION}})',
+ field: 'sum',
sortable: true
}
+ // {
+ // name: 'memo',
+ // align: 'left',
+ // label: 'Memo',
+ // field: 'memo',
+ // sortable: true
+ // }
],
pagination: {
rowsPerPage: 10
@@ -622,23 +650,15 @@ page_container %}
tokenList: function () {
const x = this.proofs
- .filter(t => t.promises?.length)
- .map(t => t.blindedMessages)
- .flat()
- .map(t => ({
- blindingFactor: t.B_,
- denomination: t.amount
- }))
- .reduce((y, t) => {
- y[`_${t.denomination}`] = y[`_${t.denomination}`] || []
- y[`_${t.denomination}`].push(t)
- return y
+ .map(t => t.amount)
+ .reduce((acc, amount) => {
+ acc[amount] = acc[amount] + amount || 1
+ return acc
}, {})
-
return Object.keys(x).map(k => ({
- denomination: x[k][0].denomination,
- count: x[k].length,
- value: x[k][0].denomination * x[k].length
+ value: k,
+ count: x[k],
+ sum: k * x[k]
}))
},
@@ -804,6 +824,7 @@ page_container %}
caption: '400 BAD REQUEST'
})
this.parse.show = false
+ throw error
return
}
@@ -891,6 +912,7 @@ page_container %}
},
showInvoiceDialog: function (data) {
+ console.log('##### showInvoiceDialog')
this.invoiceData = _.clone(data)
this.showInvoiceDetails = true
},
@@ -915,29 +937,25 @@ page_container %}
this.showReceiveTokens = true
},
- recheckPendingInvoices: async function () {
- for (const invoice of this.invoicesCashu) {
- if (invoice.status === 'pending') {
- this.recheckInvoice(invoice.hash)
- // try {
- // const {data} = await LNbits.api.request(
- // 'POST',
- // `/cashu/api/v1/${this.mintId}/mint?payment_hash=${invoice.hash}`,
- // '',
- // {
- // blinded_messages: []
- // }
- // )
- // console.log('### data', data)
- // } catch (error) {
- // console.error(error)
- // LNbits.utils.notifyApiError(error)
- // }
- }
- }
- },
-
//////////////////////// MINT //////////////////////////////////////////
+ requestMintButton: async function () {
+ await this.requestMint()
+ console.log('this is your invoice BEFORE')
+ console.log(this.invoiceData)
+ this.invoiceCheckListener = setInterval(async () => {
+ try {
+ console.log('this is your invoice AFTER')
+ console.log(this.invoiceData)
+ await this.recheckInvoice(this.invoiceData.hash, false)
+ clearInterval(this.invoiceCheckListener)
+ this.invoiceData.bolt11 = ''
+ this.showInvoiceDetails = false
+ this.fetchBalance()
+ } catch (error) {
+ console.log('not paid yet')
+ }
+ }, 3000)
+ },
requestMint: async function () {
// gets an invoice from the mint to get new tokens
@@ -961,9 +979,10 @@ page_container %}
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
+ throw error
}
},
- mintApi: async function (amounts, payment_hash) {
+ mintApi: async function (amounts, payment_hash, verbose = true) {
console.log('### promises', payment_hash)
try {
let secrets = await this.generateSecrets(amounts)
@@ -984,19 +1003,29 @@ page_container %}
return proofs
} catch (error) {
console.error(error)
- LNbits.utils.notifyApiError(error)
+ if (verbose) {
+ LNbits.utils.notifyApiError(error)
+ }
+ throw error
}
},
- mint: async function (amount, payment_hash) {
+ mint: async function (amount, payment_hash, verbose = true) {
try {
const split = splitAmount(amount)
- const proofs = await this.mintApi(split, payment_hash)
+ const proofs = await this.mintApi(split, payment_hash, verbose)
+ if (!proofs.length) {
+ throw 'could not mint'
+ }
this.proofs.push(...proofs)
this.storeProofs()
await this.setInvoicePaid(payment_hash)
+ return proofs
} catch (error) {
console.error(error)
- LNbits.utils.notifyApiError(error)
+ if (verbose) {
+ LNbits.utils.notifyApiError(error)
+ }
+ throw error
}
},
setInvoicePaid: async function (payment_hash) {
@@ -1004,10 +1033,16 @@ page_container %}
invoice.status = 'paid'
this.storeinvoicesCashu()
},
- recheckInvoice: async function (payment_hash) {
+ recheckInvoice: async function (payment_hash, verbose = true) {
console.log('### recheckInvoice.hash', payment_hash)
const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
- this.mint(invoice.amount, invoice.hash)
+ try {
+ proofs = await this.mint(invoice.amount, invoice.hash, verbose)
+ return proofs
+ } catch (error) {
+ console.log('Invoice still pending')
+ throw error
+ }
},
// requestTokens: async function (amounts, paymentHash) {
@@ -1100,6 +1135,7 @@ page_container %}
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
+ throw error
}
},
@@ -1120,6 +1156,7 @@ page_container %}
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
+ throw error
}
},
splitApi: async function (proofs, amount) {
@@ -1176,79 +1213,27 @@ page_container %}
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
+ throw error
}
},
- ////////////////////////////////////////////////////////////////////////////////////
-
- receiveTokens: async function () {
+ redeem: async function () {
this.showReceiveTokens = false
console.log('### receive tokens', this.receiveData.tokensBase64)
- if (this.receiveData.tokensBase64) {
+ try {
+ if (this.receiveData.tokensBase64.length == 0) {
+ throw new Error('no tokens provided.')
+ }
const tokensJson = atob(this.receiveData.tokensBase64)
const proofs = JSON.parse(tokensJson)
const amount = proofs.reduce((s, t) => (s += t.amount), 0)
- const amounts = splitAmount(amount)
- const newTokens = await this.buildTokens(amounts)
- console.log('newTokens', newTokens)
-
- const payload = {
- amount,
- proofs,
- outputs: {
- blinded_messages: newTokens.blindedMessages
- }
- }
-
- console.log('payload', JSON.stringify(payload))
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/split`,
- '',
- payload
- )
-
- newTokens.promises = data.snd
- // console.log('split data', JSON.stringify(data.snd))
- // for (let i =0 ;i < newTokens.length; i++) {
- // Object.assign(newTokens[i], promises)
- // }
- console.log('newTokens 2', newTokens)
- this.proofs.push(newTokens)
- this.storeProofs()
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- }
+ let {fristProofs, scndProofs} = await this.split(proofs, amount)
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
}
- },
-
- buildTokens: async function (amounts, paymentHash) {
- const blindedMessages = []
- const secrets = []
- const rs = []
- for (let i = 0; i < amounts.length; i++) {
- const secret = nobleSecp256k1.utils.randomBytes(32)
- // const secret = nobleSecp256k1.utils.hexToBytes(
- // '0000000000000000000000000000000000000000000000000000000000000000'
- // )
- // todo: base64Url
- const encodedSecret = uint8ToBase64.encode(secret)
- secrets.push(encodedSecret)
- const {B_, r} = await step1Alice(secret)
- rs.push(r)
- blindedMessages.push({amount: amounts[i], B_: B_})
- }
-
- const newTokens = {
- hash: paymentHash,
- blindedMessages,
- rs,
- secrets,
- status: 'pending'
- }
- return newTokens
+ // }
},
sendTokens: async function () {
@@ -1257,68 +1242,173 @@ page_container %}
this.proofs,
this.sendData.amount
)
-
- // const amounts = splitAmount(this.sendData.amount)
- // const sendTokens = []
- // sendTokens.push(this.proofs)
- // for (const amount of amounts) {
- // const token = this.findTokenForAmount(amount)
- // if (token) {
- // sendTokens.push(token)
- // } else {
- // this.$q.notify({
- // timeout: 5000,
- // type: 'warning',
- // message: `Cannot select amount for denomination ${amount}`
- // })
- // this.sendData.tokens = ''
- // this.sendData.tokensBase64 = ''
- // return
- // }
- // }
this.sendData.tokens = ''
this.sendData.tokensBase64 = ''
- // console.log('### sendTokens', sendTokens)
- // this.sendData.tokens = sendTokens.map((token, tokenIndex) => {
- // return this.promiseToProof(
- // token.promises[tokenIndex].amount,
- // token.promises[tokenIndex]['C_'],
- // token.promises[tokenIndex].secret,
- // token.promises[tokenIndex].r
- // )
- // })
this.sendData.tokens = scndProofs
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
+
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
+ this.storeProofs()
},
- burnTokens: function () {
- for (const sentToken of this.sendData.tokens) {
- for (const token of this.proofs) {
- if (token.status === 'paid') {
- const secretIndex = token.secrets.findIndex(
- s => s === sentToken.secret
- )
- console.log('### secretIndex', secretIndex)
- if (secretIndex >= 0) {
- token.blindedMessages?.splice(secretIndex, 1)
- token.promises?.splice(secretIndex, 1)
- token.rs?.splice(secretIndex, 1)
- token.secrets?.splice(secretIndex, 1)
- }
- }
+ melt: async function () {
+ console.log('#### sell tokens')
+ const amount = this.payInvoiceData.invoice.sat
+ const paidTokens = this.proofs.filter(t => t.promises?.length)
+ console.log('### paidTokens', paidTokens)
+ const proofs = paidTokens.map(token => {
+ return token.promises.map((promise, promiseIndex) => {
+ console.log('### promise', promise)
+
+ const secret = token.secrets[promiseIndex]
+ const r = token.rs[promiseIndex]
+
+ return this.promiseToProof(promise.amount, promise['C_'], secret, r)
+ })
+ })
+ const payload = {
+ proofs: proofs.flat(),
+ amount,
+ invoice: this.payInvoiceData.bolt11
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/melt`,
+ '',
+ payload
+ )
+ this.$q.notify({
+ timeout: 5000,
+ message: 'Invoice paid'
+ })
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+
+ recheckPendingInvoices: async function () {
+ for (const invoice of this.invoicesCashu) {
+ if (invoice.status === 'pending') {
+ this.recheckInvoice(invoice.hash, false)
}
}
-
- this.$q.notify({
- timeout: 5000,
- message: 'Tokens burned'
- })
- this.storeProofs()
- this.showSendTokens = false
- console.log('### this.proofs', this.proofs)
},
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // receiveTokens: async function () {
+ // this.showReceiveTokens = false
+ // console.log('### receive tokens', this.receiveData.tokensBase64)
+ // // if (this.receiveData.tokensBase64) {
+ // // const tokensJson = atob(this.receiveData.tokensBase64)
+ // // const proofs = JSON.parse(tokensJson)
+ // // const amount = proofs.reduce((s, t) => (s += t.amount), 0)
+ // // const amounts = splitAmount(amount)
+ // // const newTokens = await this.buildTokens(amounts)
+ // // console.log('newTokens', newTokens)
+
+ // // const payload = {
+ // // amount,
+ // // proofs,
+ // // outputs: {
+ // // blinded_messages: newTokens.blindedMessages
+ // // }
+ // // }
+
+ // // console.log('payload', JSON.stringify(payload))
+ // try {
+ // if (this.receiveData.tokensBase64.length == 0) {
+ // throw new Error('no tokens provided.')
+ // }
+ // const tokensJson = atob(this.receiveData.tokensBase64)
+ // const proofs = JSON.parse(tokensJson)
+ // const amount = proofs.reduce((s, t) => (s += t.amount), 0)
+ // let {fristProofs, scndProofs} = await this.split(proofs, amount)
+ // // const {data} = await LNbits.api.request(
+ // // 'POST',
+ // // `/cashu/api/v1/${this.mintId}/split`,
+ // // '',
+ // // payload
+ // // )
+ // // newTokens.promises = data.snd
+ // // // console.log('split data', JSON.stringify(data.snd))
+ // // // for (let i =0 ;i < newTokens.length; i++) {
+ // // // Object.assign(newTokens[i], promises)
+ // // // }
+ // // console.log('newTokens 2', newTokens)
+ // // this.proofs.push(newTokens)
+ // // this.storeProofs()
+ // } catch (error) {
+ // console.error(error)
+ // LNbits.utils.notifyApiError(error)
+ // }
+ // // }
+ // },
+
+ // buildTokens: async function (amounts, paymentHash) {
+ // const blindedMessages = []
+ // const secrets = []
+ // const rs = []
+ // for (let i = 0; i < amounts.length; i++) {
+ // const secret = nobleSecp256k1.utils.randomBytes(32)
+ // // const secret = nobleSecp256k1.utils.hexToBytes(
+ // // '0000000000000000000000000000000000000000000000000000000000000000'
+ // // )
+ // // todo: base64Url
+ // const encodedSecret = uint8ToBase64.encode(secret)
+ // secrets.push(encodedSecret)
+ // const {B_, r} = await step1Alice(secret)
+ // rs.push(r)
+ // blindedMessages.push({amount: amounts[i], B_: B_})
+ // }
+
+ // const newTokens = {
+ // hash: paymentHash,
+ // blindedMessages,
+ // rs,
+ // secrets,
+ // status: 'pending'
+ // }
+ // return newTokens
+ // },
+
+ // burnTokens: function () {
+ // for (const sentToken of this.sendData.tokens) {
+ // for (const token of this.proofs) {
+ // if (token.status === 'paid') {
+ // const secretIndex = token.secrets.findIndex(
+ // s => s === sentToken.secret
+ // )
+ // console.log('### secretIndex', secretIndex)
+ // if (secretIndex >= 0) {
+ // token.blindedMessages?.splice(secretIndex, 1)
+ // token.promises?.splice(secretIndex, 1)
+ // token.rs?.splice(secretIndex, 1)
+ // token.secrets?.splice(secretIndex, 1)
+ // }
+ // }
+ // }
+ // }
+
+ // this.$q.notify({
+ // timeout: 5000,
+ // message: 'Tokens burned'
+ // })
+ // this.storeProofs()
+ // this.showSendTokens = false
+ // console.log('### this.proofs', this.proofs)
+ // },
+
findTokenForAmount: function (amount) {
for (const token of this.proofs) {
const index = token.promises?.findIndex(p => p.amount === amount)
@@ -1377,46 +1467,47 @@ page_container %}
message: 'Cannot decode invoice',
caption: error + ''
})
+ throw error
}
},
- melt: async function () {
- console.log('#### sell tokens')
- const amount = this.payInvoiceData.invoice.sat
- const paidTokens = this.proofs.filter(t => t.promises?.length)
- console.log('### paidTokens', paidTokens)
- const proofs = paidTokens.map(token => {
- return token.promises.map((promise, promiseIndex) => {
- console.log('### promise', promise)
+ // melt: async function () {
+ // console.log('#### sell tokens')
+ // const amount = this.payInvoiceData.invoice.sat
+ // const paidTokens = this.proofs.filter(t => t.promises?.length)
+ // console.log('### paidTokens', paidTokens)
+ // const proofs = paidTokens.map(token => {
+ // return token.promises.map((promise, promiseIndex) => {
+ // console.log('### promise', promise)
- const secret = token.secrets[promiseIndex]
- const r = token.rs[promiseIndex]
+ // const secret = token.secrets[promiseIndex]
+ // const r = token.rs[promiseIndex]
- return this.promiseToProof(promise.amount, promise['C_'], secret, r)
- })
- })
- const payload = {
- proofs: proofs.flat(),
- amount,
- invoice: this.payInvoiceData.bolt11
- }
- console.log('#### payload', JSON.stringify(payload))
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/melt`,
- '',
- payload
- )
- this.$q.notify({
- timeout: 5000,
- message: 'Invoice paid'
- })
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- }
- },
+ // return this.promiseToProof(promise.amount, promise['C_'], secret, r)
+ // })
+ // })
+ // const payload = {
+ // proofs: proofs.flat(),
+ // amount,
+ // invoice: this.payInvoiceData.bolt11
+ // }
+ // console.log('#### payload', JSON.stringify(payload))
+ // try {
+ // const {data} = await LNbits.api.request(
+ // 'POST',
+ // `/cashu/api/v1/${this.mintId}/melt`,
+ // '',
+ // payload
+ // )
+ // this.$q.notify({
+ // timeout: 5000,
+ // message: 'Invoice paid'
+ // })
+ // } catch (error) {
+ // console.error(error)
+ // LNbits.utils.notifyApiError(error)
+ // }
+ // },
// C_hex = promise['C_']
// amount = promise.amount
@@ -1445,7 +1536,7 @@ page_container %}
},
watch: {
payments: function () {
- this.fetchBalance()
+ this.balance()
}
},
From 9b83733029c07042f14ac81caf54b97703ab3522 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 15:40:29 +0100
Subject: [PATCH 0298/1058] fix manifest
---
.../cashu/templates/cashu/_cashu.html | 5 +--
.../cashu/templates/cashu/wallet.html | 1 +
lnbits/extensions/cashu/views.py | 35 ++++++++++++-------
3 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/_cashu.html b/lnbits/extensions/cashu/templates/cashu/_cashu.html
index 0c7c4338..952fe7e1 100644
--- a/lnbits/extensions/cashu/templates/cashu/_cashu.html
+++ b/lnbits/extensions/cashu/templates/cashu/_cashu.html
@@ -1,10 +1,7 @@
-
- Make Ecash mints with peg in/out to a wallet, that can create and manage
- ecash.
-
+ Create Cashu ecash mints and wallets.
Created by
arcbtc ,
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3a9c48db..e4621e11 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -180,6 +180,7 @@ page_container %}
Date: Fri, 4 Nov 2022 16:16:15 +0100
Subject: [PATCH 0299/1058] trying to get camera to run
---
.../cashu/templates/cashu/wallet.html | 257 ++++++++++++++++--
1 file changed, 238 insertions(+), 19 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index e4621e11..110ecfa2 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -45,7 +45,7 @@ page_container %}
-
+
Receive Tokens
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+ {% raw %} {{ parseFloat(String(parse.invoice.fsat).replaceAll(",",
+ "")) / 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
+
+
+ {{ parse.invoice.fsat }}{% endraw %} {{LNBITS_DENOMINATION}} {%
+ raw %}
+
+
+
+ Description: {{ parse.invoice.description }}
+ Expire date: {{ parse.invoice.expireDate }}
+ Hash: {{ parse.invoice.hash }}
+
+ {% endraw %}
+
+ Pay
+ Cancel
+
+
+ Not enough funds!
+ Cancel
+
+
+
+ {% raw %}
+
+
+ Authenticate with {{ parse.lnurlauth.domain }} ?
+
+
+
+ For every website and for every LNbits wallet, a new keypair
+ will be deterministically generated so your identity can't be
+ tied to your LNbits wallet or linked across websites. No other
+ data will be shared with {{ parse.lnurlauth.domain }}.
+
+ Your public key for {{ parse.lnurlauth.domain }} is:
+
+ {{ parse.lnurlauth.pubkey }}
+
+
+ Login
+ Cancel
+
+
+ {% endraw %}
+
+
+ {% raw %}
+
+
+ {{ parse.lnurlpay.domain }} is requesting {{
+ parse.lnurlpay.maxSendable | msatoshiFormat }}
+ {{LNBITS_DENOMINATION}}
+
+
+ and a {{parse.lnurlpay.commentAllowed}}-char comment
+
+
+
+ {{ parse.lnurlpay.targetUser || parse.lnurlpay.domain }}
+ is requesting
+ between
+ {{ parse.lnurlpay.minSendable | msatoshiFormat }} and
+ {{ parse.lnurlpay.maxSendable | msatoshiFormat }}
+ {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
+
+
+ and a {{parse.lnurlpay.commentAllowed}}-char comment
+
+
+
+
+
+ {{ parse.lnurlpay.description }}
+
+
+
+
+
+
+
+ {% endraw %}
+
+ {% raw %}
+
+
+
+
+
+
+ Send {{LNBITS_DENOMINATION}}
+ Cancel
+
+
+ {% endraw %}
+
+
+
+
+
+
+ Read
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
Warning
@@ -493,7 +693,11 @@ page_container %}
invoiceCheckListener: () => {},
payInvoiceData: {
invoice: '',
- bolt11: ''
+ bolt11: '',
+ camera: {
+ show: false,
+ camera: 'auto'
+ }
},
sendData: {
amount: 0,
@@ -923,6 +1127,7 @@ page_container %}
this.payInvoiceData.invoice = ''
this.payInvoiceData.bolt11 = ''
this.showPayInvoice = true
+ this.payInvoiceData.camera.show = false
},
showSendTokensDialog: function () {
@@ -1131,6 +1336,10 @@ page_container %}
this.proofs[i].reserved = true
}
}
+
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
this.storeProofs()
return {fristProofs, scndProofs}
} catch (error) {
@@ -1250,28 +1459,37 @@ page_container %}
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
// delete tokens from db
- this.proofs = fristProofs
+ // this.proofs = fristProofs
// add new fristProofs, scndProofs to this.proofs
- this.storeProofs()
+ // this.storeProofs()
},
melt: async function () {
- console.log('#### sell tokens')
+ console.log('#### pay lightning')
const amount = this.payInvoiceData.invoice.sat
- const paidTokens = this.proofs.filter(t => t.promises?.length)
- console.log('### paidTokens', paidTokens)
- const proofs = paidTokens.map(token => {
- return token.promises.map((promise, promiseIndex) => {
- console.log('### promise', promise)
+ // if (amount > balance()) {
+ // LNbits.utils.notifyApiError('Balance too low')
+ // return
+ // }
+ let {fristProofs, scndProofs} = await this.splitToSend(
+ this.proofs,
+ amount
+ )
- const secret = token.secrets[promiseIndex]
- const r = token.rs[promiseIndex]
+ // const paidTokens = this.proofs.filter(t => t.promises?.length)
+ // console.log('### paidTokens', paidTokens)
+ // const proofs = paidTokens.map(token => {
+ // return token.promises.map((promise, promiseIndex) => {
+ // console.log('### promise', promise)
- return this.promiseToProof(promise.amount, promise['C_'], secret, r)
- })
- })
+ // const secret = token.secrets[promiseIndex]
+ // const r = token.rs[promiseIndex]
+
+ // return this.promiseToProof(promise.amount, promise['C_'], secret, r)
+ // })
+ // })
const payload = {
- proofs: proofs.flat(),
+ proofs: scndProofs.flat(),
amount,
invoice: this.payInvoiceData.bolt11
}
@@ -1285,6 +1503,7 @@ page_container %}
)
this.$q.notify({
timeout: 5000,
+ type: 'positive',
message: 'Invoice paid'
})
} catch (error) {
From fda57a5089495fed9de9b8e26811328b73c263fb Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 16:44:17 +0100
Subject: [PATCH 0300/1058] works
---
.../cashu/templates/cashu/wallet.html | 189 +++++++++++-------
1 file changed, 117 insertions(+), 72 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 110ecfa2..56104d50 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -202,22 +202,25 @@ page_container %}
-
+
-
+
- {% raw %} {{ parseFloat(String(parse.invoice.fsat).replaceAll(",",
+ {% raw %} {{
+ parseFloat(String(payInvoiceData.invoice.fsat).replaceAll(",",
"")) / 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
- {{ parse.invoice.fsat }}{% endraw %} {{LNBITS_DENOMINATION}} {%
- raw %}
+ {{ payInvoiceData.invoice.fsat }}{% endraw %}
+ {{LNBITS_DENOMINATION}} {% raw %}
- Description: {{ parse.invoice.description }}
- Expire date: {{ parse.invoice.expireDate }}
- Hash: {{ parse.invoice.hash }}
+ Description: {{
+ payInvoiceData.invoice.description }}
+ Expire date: {{ payInvoiceData.invoice.expireDate
+ }}
+ Hash: {{ payInvoiceData.invoice.hash }}
{% endraw %}
@@ -235,22 +238,27 @@ page_container %}
>
-
+
{% raw %}
- Authenticate with {{ parse.lnurlauth.domain }} ?
+ Authenticate with {{ payInvoiceData.lnurlauth.domain }} ?
For every website and for every LNbits wallet, a new keypair
will be deterministically generated so your identity can't be
tied to your LNbits wallet or linked across websites. No other
- data will be shared with {{ parse.lnurlauth.domain }}.
+ data will be shared with {{ payInvoiceData.lnurlauth.domain }}.
+
+
+ Your public key for
+ {{ payInvoiceData.lnurlauth.domain }} is:
- Your public key for {{ parse.lnurlauth.domain }} is:
- {{ parse.lnurlauth.pubkey }}
+
+ {{ payInvoiceData.lnurlauth.pubkey }}
+
Login
@@ -261,37 +269,45 @@ page_container %}
{% endraw %}
-
+
{% raw %}
-
- {{ parse.lnurlpay.domain }} is requesting {{
- parse.lnurlpay.maxSendable | msatoshiFormat }}
+
+ {{ payInvoiceData.lnurlpay.domain }} is requesting {{
+ payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }}
{{LNBITS_DENOMINATION}}
-
+
- and a {{parse.lnurlpay.commentAllowed}}-char comment
+ and a {{payInvoiceData.lnurlpay.commentAllowed}}-char comment
- {{ parse.lnurlpay.targetUser || parse.lnurlpay.domain }}
+ {{ payInvoiceData.lnurlpay.targetUser ||
+ payInvoiceData.lnurlpay.domain }}
is requesting
between
- {{ parse.lnurlpay.minSendable | msatoshiFormat }} and
- {{ parse.lnurlpay.maxSendable | msatoshiFormat }}
+ {{ payInvoiceData.lnurlpay.minSendable | msatoshiFormat }}
+ and
+ {{ payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }}
{% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
-
+
- and a {{parse.lnurlpay.commentAllowed}}-char comment
+ and a {{payInvoiceData.lnurlpay.commentAllowed}}-char comment
- {{ parse.lnurlpay.description }}
+ {{ payInvoiceData.lnurlpay.description }}
-
-
+
+
@@ -300,26 +316,26 @@ page_container %}
{% raw %}
@@ -336,14 +352,15 @@ page_container %}
@@ -352,7 +369,7 @@ page_container %}
Read
@@ -378,7 +395,7 @@ page_container %}
-
+
{},
payInvoiceData: {
- invoice: '',
+ // invoice: '',
bolt11: '',
+ // camera: {
+ // show: false,
+ // camera: 'auto'
+ // }
+ show: false,
+ invoice: null,
+ lnurlpay: null,
+ lnurlauth: null,
+ data: {
+ request: '',
+ amount: 0,
+ comment: ''
+ },
+ paymentChecker: null,
camera: {
show: false,
camera: 'auto'
@@ -846,8 +878,8 @@ page_container %}
},
canPay: function () {
- if (!this.parse.invoice) return false
- return this.parse.invoice.sat <= this.balance
+ if (!this.payInvoiceData.invoice) return false
+ return this.payInvoiceData.invoice.sat <= this.balance
},
pendingPaymentsExist: function () {
return this.payments.findIndex(payment => payment.pending) !== -1
@@ -884,10 +916,10 @@ page_container %}
return row.payment_hash + row.amount
},
closeCamera: function () {
- this.parse.camera.show = false
+ this.payInvoiceData.camera.show = false
},
showCamera: function () {
- this.parse.camera.show = true
+ this.payInvoiceData.camera.show = true
},
showChart: function () {
this.paymentsChart.show = true
@@ -912,14 +944,15 @@ page_container %}
this.focusInput('setAmount')
},
showParseDialog: function () {
- this.parse.show = true
- this.parse.invoice = null
- this.parse.lnurlpay = null
- this.parse.lnurlauth = null
- this.parse.data.request = ''
- this.parse.data.comment = ''
- this.parse.data.paymentChecker = null
- this.parse.camera.show = false
+ this.payInvoiceData.show = true
+ this.payInvoiceData.invoice = null
+ this.payInvoiceData.lnurlpay = null
+ this.payInvoiceData.lnurlauth = null
+ this.payInvoiceData.data.request = ''
+ this.payInvoiceData.data.comment = ''
+ this.payInvoiceData.data.paymentChecker = null
+ this.payInvoiceData.camera.show = false
+ this.focusInput('pasteInput')
},
closeReceiveDialog: function () {
@@ -929,7 +962,7 @@ page_container %}
},
closeParseDialog: function () {
setTimeout(() => {
- clearInterval(this.parse.paymentChecker)
+ clearInterval(this.payInvoiceData.paymentChecker)
}, 10000)
},
onPaymentReceived: function (paymentHash) {
@@ -994,33 +1027,43 @@ page_container %}
})
},
decodeQR: function (res) {
- this.parse.data.request = res
+ this.payInvoiceData.data.request = res
this.decodeRequest()
- this.parse.camera.show = false
+ this.payInvoiceData.camera.show = false
},
decodeRequest: function () {
- this.parse.show = true
- let req = this.parse.data.request.toLowerCase()
- if (this.parse.data.request.toLowerCase().startsWith('lightning:')) {
- this.parse.data.request = this.parse.data.request.slice(10)
- } else if (this.parse.data.request.toLowerCase().startsWith('lnurl:')) {
- this.parse.data.request = this.parse.data.request.slice(6)
+ this.payInvoiceData.show = true
+ let req = this.payInvoiceData.data.request.toLowerCase()
+ if (
+ this.payInvoiceData.data.request
+ .toLowerCase()
+ .startsWith('lightning:')
+ ) {
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
+ 10
+ )
+ } else if (
+ this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl:')
+ ) {
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
+ 6
+ )
} else if (req.indexOf('lightning=lnurl1') !== -1) {
- this.parse.data.request = this.parse.data.request
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request
.split('lightning=')[1]
.split('&')[0]
}
if (
- this.parse.data.request.toLowerCase().startsWith('lnurl1') ||
- this.parse.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
+ this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl1') ||
+ this.payInvoiceData.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
) {
return
}
let invoice
try {
- invoice = decode(this.parse.data.request)
+ invoice = decode(this.payInvoiceData.data.request)
} catch (error) {
this.$q.notify({
timeout: 3000,
@@ -1028,7 +1071,7 @@ page_container %}
message: error + '.',
caption: '400 BAD REQUEST'
})
- this.parse.show = false
+ this.payInvoiceData.show = false
throw error
return
}
@@ -1060,7 +1103,7 @@ page_container %}
}
})
- this.parse.invoice = Object.freeze(cleanInvoice)
+ this.payInvoiceData.invoice = Object.freeze(cleanInvoice)
},
payInvoice: function () {
let dismissPaymentMsg = this.$q.notify({
@@ -1125,7 +1168,7 @@ page_container %}
showPayInvoiceDialog: function () {
console.log('### showPayInvoiceDialog')
this.payInvoiceData.invoice = ''
- this.payInvoiceData.bolt11 = ''
+ this.payInvoiceData.data.request = ''
this.showPayInvoice = true
this.payInvoiceData.camera.show = false
},
@@ -1491,7 +1534,7 @@ page_container %}
const payload = {
proofs: scndProofs.flat(),
amount,
- invoice: this.payInvoiceData.bolt11
+ invoice: this.payInvoiceData.data.request
}
console.log('#### payload', JSON.stringify(payload))
try {
@@ -1506,6 +1549,8 @@ page_container %}
type: 'positive',
message: 'Invoice paid'
})
+ this.payInvoiceData.invoice = null
+ this.payInvoiceData.show = false
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
@@ -1645,7 +1690,7 @@ page_container %}
checkInvoice: function () {
console.log('#### checkInvoice')
try {
- const invoice = decode(this.payInvoiceData.bolt11)
+ const invoice = decode(this.payInvoiceData.data.request)
const cleanInvoice = {
msat: invoice.human_readable_part.amount,
@@ -1709,7 +1754,7 @@ page_container %}
// const payload = {
// proofs: proofs.flat(),
// amount,
- // invoice: this.payInvoiceData.bolt11
+ // invoice: this.payInvoiceData.data.request
// }
// console.log('#### payload', JSON.stringify(payload))
// try {
From 1c13a3757c1ee743fb8f31fb5c73dfae5c52319b Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 17:17:11 +0100
Subject: [PATCH 0301/1058] works
---
.../cashu/templates/cashu/wallet.html | 177 +-----------------
lnbits/extensions/cashu/views.py | 6 +-
2 files changed, 9 insertions(+), 174 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 56104d50..efd3ded3 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1199,7 +1199,11 @@ page_container %}
clearInterval(this.invoiceCheckListener)
this.invoiceData.bolt11 = ''
this.showInvoiceDetails = false
- this.fetchBalance()
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Payment received'
+ })
} catch (error) {
console.log('not paid yet')
}
@@ -1294,14 +1298,6 @@ page_container %}
}
},
- // requestTokens: async function (amounts, paymentHash) {
- // const newTokens = await this.buildTokens(amounts, paymentHash)
- // // this.proofs.push(newTokens)
- // // this.storeProofs()
- // // console.log('### this.proofs', this.proofs)
- // // await this.mint(paymentHash, newTokens.newTokens)
- // },
-
generateSecrets: async function (amounts) {
const secrets = []
for (let i = 0; i < amounts.length; i++) {
@@ -1500,14 +1496,10 @@ page_container %}
this.sendData.tokens = scndProofs
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
-
- // delete tokens from db
- // this.proofs = fristProofs
- // add new fristProofs, scndProofs to this.proofs
- // this.storeProofs()
},
melt: async function () {
+ // todo: get fees from server and add to inputs
console.log('#### pay lightning')
const amount = this.payInvoiceData.invoice.sat
// if (amount > balance()) {
@@ -1518,19 +1510,6 @@ page_container %}
this.proofs,
amount
)
-
- // const paidTokens = this.proofs.filter(t => t.promises?.length)
- // console.log('### paidTokens', paidTokens)
- // const proofs = paidTokens.map(token => {
- // return token.promises.map((promise, promiseIndex) => {
- // console.log('### promise', promise)
-
- // const secret = token.secrets[promiseIndex]
- // const r = token.rs[promiseIndex]
-
- // return this.promiseToProof(promise.amount, promise['C_'], secret, r)
- // })
- // })
const payload = {
proofs: scndProofs.flat(),
amount,
@@ -1571,109 +1550,6 @@ page_container %}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // receiveTokens: async function () {
- // this.showReceiveTokens = false
- // console.log('### receive tokens', this.receiveData.tokensBase64)
- // // if (this.receiveData.tokensBase64) {
- // // const tokensJson = atob(this.receiveData.tokensBase64)
- // // const proofs = JSON.parse(tokensJson)
- // // const amount = proofs.reduce((s, t) => (s += t.amount), 0)
- // // const amounts = splitAmount(amount)
- // // const newTokens = await this.buildTokens(amounts)
- // // console.log('newTokens', newTokens)
-
- // // const payload = {
- // // amount,
- // // proofs,
- // // outputs: {
- // // blinded_messages: newTokens.blindedMessages
- // // }
- // // }
-
- // // console.log('payload', JSON.stringify(payload))
- // try {
- // if (this.receiveData.tokensBase64.length == 0) {
- // throw new Error('no tokens provided.')
- // }
- // const tokensJson = atob(this.receiveData.tokensBase64)
- // const proofs = JSON.parse(tokensJson)
- // const amount = proofs.reduce((s, t) => (s += t.amount), 0)
- // let {fristProofs, scndProofs} = await this.split(proofs, amount)
- // // const {data} = await LNbits.api.request(
- // // 'POST',
- // // `/cashu/api/v1/${this.mintId}/split`,
- // // '',
- // // payload
- // // )
- // // newTokens.promises = data.snd
- // // // console.log('split data', JSON.stringify(data.snd))
- // // // for (let i =0 ;i < newTokens.length; i++) {
- // // // Object.assign(newTokens[i], promises)
- // // // }
- // // console.log('newTokens 2', newTokens)
- // // this.proofs.push(newTokens)
- // // this.storeProofs()
- // } catch (error) {
- // console.error(error)
- // LNbits.utils.notifyApiError(error)
- // }
- // // }
- // },
-
- // buildTokens: async function (amounts, paymentHash) {
- // const blindedMessages = []
- // const secrets = []
- // const rs = []
- // for (let i = 0; i < amounts.length; i++) {
- // const secret = nobleSecp256k1.utils.randomBytes(32)
- // // const secret = nobleSecp256k1.utils.hexToBytes(
- // // '0000000000000000000000000000000000000000000000000000000000000000'
- // // )
- // // todo: base64Url
- // const encodedSecret = uint8ToBase64.encode(secret)
- // secrets.push(encodedSecret)
- // const {B_, r} = await step1Alice(secret)
- // rs.push(r)
- // blindedMessages.push({amount: amounts[i], B_: B_})
- // }
-
- // const newTokens = {
- // hash: paymentHash,
- // blindedMessages,
- // rs,
- // secrets,
- // status: 'pending'
- // }
- // return newTokens
- // },
-
- // burnTokens: function () {
- // for (const sentToken of this.sendData.tokens) {
- // for (const token of this.proofs) {
- // if (token.status === 'paid') {
- // const secretIndex = token.secrets.findIndex(
- // s => s === sentToken.secret
- // )
- // console.log('### secretIndex', secretIndex)
- // if (secretIndex >= 0) {
- // token.blindedMessages?.splice(secretIndex, 1)
- // token.promises?.splice(secretIndex, 1)
- // token.rs?.splice(secretIndex, 1)
- // token.secrets?.splice(secretIndex, 1)
- // }
- // }
- // }
- // }
-
- // this.$q.notify({
- // timeout: 5000,
- // message: 'Tokens burned'
- // })
- // this.storeProofs()
- // this.showSendTokens = false
- // console.log('### this.proofs', this.proofs)
- // },
-
findTokenForAmount: function (amount) {
for (const token of this.proofs) {
const index = token.promises?.findIndex(p => p.amount === amount)
@@ -1736,47 +1612,6 @@ page_container %}
}
},
- // melt: async function () {
- // console.log('#### sell tokens')
- // const amount = this.payInvoiceData.invoice.sat
- // const paidTokens = this.proofs.filter(t => t.promises?.length)
- // console.log('### paidTokens', paidTokens)
- // const proofs = paidTokens.map(token => {
- // return token.promises.map((promise, promiseIndex) => {
- // console.log('### promise', promise)
-
- // const secret = token.secrets[promiseIndex]
- // const r = token.rs[promiseIndex]
-
- // return this.promiseToProof(promise.amount, promise['C_'], secret, r)
- // })
- // })
- // const payload = {
- // proofs: proofs.flat(),
- // amount,
- // invoice: this.payInvoiceData.data.request
- // }
- // console.log('#### payload', JSON.stringify(payload))
- // try {
- // const {data} = await LNbits.api.request(
- // 'POST',
- // `/cashu/api/v1/${this.mintId}/melt`,
- // '',
- // payload
- // )
- // this.$q.notify({
- // timeout: 5000,
- // message: 'Invoice paid'
- // })
- // } catch (error) {
- // console.error(error)
- // LNbits.utils.notifyApiError(error)
- // }
- // },
-
- // C_hex = promise['C_']
- // amount = promise.amount
-
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
'GET',
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 17e39cbe..b254d615 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -56,8 +56,8 @@ async def manifest(cashu_id: str):
)
return {
- "short_name": "Cashu wallet",
- "name": cashu.name + " - " + "Cashu wallet",
+ "short_name": "Cashu",
+ "name": cashu.name + " - " + "Cashu",
"icons": [
{
"src": LNBITS_CUSTOM_LOGO
@@ -71,7 +71,7 @@ async def manifest(cashu_id: str):
"background_color": "#1F2234",
"description": "Cashu ecash wallet",
"display": "standalone",
- "scope": "/cashu/wallet?mint_id=" + cashu_id,
+ "scope": "/cashu/",
"theme_color": "#1F2234",
"shortcuts": [
{
From 4e935c89d7db5f16cdf77770829f5ae4b36f95ef Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 17:17:57 +0100
Subject: [PATCH 0302/1058] make format
---
lnbits/extensions/cashu/static/js/dhke.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
index 9c7aee1b..41c2fb46 100644
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ b/lnbits/extensions/cashu/static/js/dhke.js
@@ -22,7 +22,7 @@ async function hashToCurve(secretMessage) {
async function step1Alice(secretMessage) {
// todo: document & validate `secretMessage` format
secretMessage = uint8ToBase64.encode(secretMessage)
- secretMessage = new TextEncoder().encode(secretMessage);
+ secretMessage = new TextEncoder().encode(secretMessage)
const Y = await hashToCurve(secretMessage)
const rpk = nobleSecp256k1.utils.randomPrivateKey()
const r = bytesToNumber(rpk)
From 81df70f7774147c727b0888b76c5c08b4f94110c Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 4 Nov 2022 18:46:26 +0200
Subject: [PATCH 0303/1058] fix: ugly method to refresh UI
---
.../cashu/templates/cashu/wallet.html | 29 +++++++++++++++----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index efd3ded3..e771cf9c 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -22,7 +22,7 @@ page_container %}
- {% raw %} {{balance}}
+ {% raw %} {{getBalance()}}
{{tickershort}}{% endraw %}
@@ -81,7 +81,7 @@ page_container %}
t)
+ .flat()
+ .reduce((sum, el) => (sum += el.amount), 0)
+ },
+ getTokenList: function () {
+ const x = this.proofs
+ .map(t => t.amount)
+ .reduce((acc, amount) => {
+ acc[amount] = acc[amount] + amount || 1
+ return acc
+ }, {})
+ return Object.keys(x).map(k => ({
+ value: k,
+ count: x[k],
+ sum: k * x[k]
+ }))
+ },
+
paymentTableRowKey: function (row) {
return row.payment_hash + row.amount
},
@@ -1269,7 +1289,7 @@ page_container %}
if (!proofs.length) {
throw 'could not mint'
}
- this.proofs.push(...proofs)
+ this.proofs = this.proofs.concat(proofs)
this.storeProofs()
await this.setInvoicePaid(payment_hash)
return proofs
@@ -1398,8 +1418,7 @@ page_container %}
const usedSecrets = proofs.map(p => p.secret)
this.proofs = this.proofs.filter(p => !usedSecrets.includes(p.secret))
// add new fristProofs, scndProofs to this.proofs
- this.proofs.push(...fristProofs)
- this.proofs.push(...scndProofs)
+ this.proofs = this.proofs.concat(fristProofs).concat(scndProofs)
this.storeProofs()
return {fristProofs, scndProofs}
} catch (error) {
From dd37368d982e793acad9cb477a3b81d8e68a4623 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 19:43:23 +0100
Subject: [PATCH 0304/1058] cashu update
---
poetry.lock | 78 +++++++++++++++++++++++++++++++++-----------------
pyproject.toml | 2 +-
2 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index b87b102f..28486a1a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -123,34 +123,57 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cashu"
-version = "0.4.2"
-description = "Ecash wallet and mint."
+version = "0.5.1"
+description = "Ecash wallet and mint with Bitcoin Lightning support"
category = "main"
optional = false
-python-versions = "^3.7"
-develop = false
+python-versions = ">=3.7"
[package.dependencies]
-bech32 = "^1.2.0"
-bitstring = "^3.1.9"
-click = "8.0.4"
-ecdsa = "^0.18.0"
-environs = "^9.5.0"
-fastapi = "^0.83.0"
-h11 = "0.12.0"
-loguru = "^0.6.0"
-pydantic = "^1.10.2"
-pytest-asyncio = "0.19.0"
-python-bitcoinlib = "^0.11.2"
-requests = "2.27.1"
-secp256k1 = "^0.14.0"
-SQLAlchemy = "1.3.24"
-sqlalchemy-aio = "^0.17.0"
-uvicorn = "^0.18.3"
-
-[package.source]
-type = "directory"
-url = "../cashu"
+anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+py = {version = "1.11.0", 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\""}
+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-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-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
+uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
[[package]]
name = "Cerberus"
@@ -1121,7 +1144,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "ded9ab80b3bec75bf904c3f598afbdff5f1577aaedbec25045c3b42c332f8ccc"
+content-hash = "c5d3b28864bf6b86385e38f63e3ba16d95804a812773e930b6ed818d4f09938a"
[metadata.files]
aiofiles = [
@@ -1184,7 +1207,10 @@ black = [
{file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
-cashu = []
+cashu = [
+ {file = "cashu-0.5.1-py3-none-any.whl", hash = "sha256:893f6bc098331e73cb6a5d0108c929dc7f2299d3d5405ae3b29e0868d9cd78c9"},
+ {file = "cashu-0.5.1.tar.gz", hash = "sha256:c4533c72a09b0e1439836739653d3d79a7de00a1106e6676cb8f660f894006a7"},
+]
Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
diff --git a/pyproject.toml b/pyproject.toml
index f1a52348..b493a87f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,7 +64,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
-cashu = {path = "../cashu"}
+cashu = "^0.5.1"
[tool.poetry.dev-dependencies]
From db817315e248a7b98afa776d99328cdb662460d9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 20:44:35 +0100
Subject: [PATCH 0305/1058] new icons
---
lnbits/extensions/cashu/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index b254d615..a2dd9347 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -62,7 +62,7 @@ async def manifest(cashu_id: str):
{
"src": LNBITS_CUSTOM_LOGO
if LNBITS_CUSTOM_LOGO
- else "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ else "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"type": "image/png",
"sizes": "512x512",
}
From 065657f9f7b0c28ddbf8f6fa3d5dad157b77fc37 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 20:45:12 +0100
Subject: [PATCH 0306/1058] new icons
---
lnbits/extensions/cashu/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index a2dd9347..88c09f47 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -64,7 +64,7 @@ async def manifest(cashu_id: str):
if LNBITS_CUSTOM_LOGO
else "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"type": "image/png",
- "sizes": "512x512",
+ "sizes": "96x96",
}
],
"start_url": "/cashu/wallet?mint_id=" + cashu_id,
From ea0c0805a193e6327e999d3dc71a70e3b067c64d Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 20:45:56 +0100
Subject: [PATCH 0307/1058] manifest
---
lnbits/extensions/cashu/views.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 88c09f47..6aec5cda 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -60,9 +60,7 @@ async def manifest(cashu_id: str):
"name": cashu.name + " - " + "Cashu",
"icons": [
{
- "src": LNBITS_CUSTOM_LOGO
- if LNBITS_CUSTOM_LOGO
- else "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"type": "image/png",
"sizes": "96x96",
}
From 6735a08f2ce9106498067a24859a784960af5115 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 4 Nov 2022 20:48:32 +0100
Subject: [PATCH 0308/1058] manifest
---
lnbits/extensions/cashu/views.py | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 6aec5cda..ca05d83e 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -59,11 +59,16 @@ async def manifest(cashu_id: str):
"short_name": "Cashu",
"name": cashu.name + " - " + "Cashu",
"icons": [
+ {
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "type": "image/png",
+ "sizes": "512x512",
+ },
{
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"type": "image/png",
"sizes": "96x96",
- }
+ },
],
"start_url": "/cashu/wallet?mint_id=" + cashu_id,
"background_color": "#1F2234",
@@ -77,6 +82,16 @@ async def manifest(cashu_id: str):
"short_name": cashu.name,
"description": cashu.name + " - " + "Cashu wallet",
"url": "/cashu/wallet?mint_id=" + cashu_id,
+ "icons": [
+ {
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
+ "sizes": "96x96",
+ },
+ {
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "sizes": "512x512",
+ },
+ ],
}
],
}
From bd9bc0f070305da4c32fbcee59890c287cd75785 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 05:38:29 +0100
Subject: [PATCH 0309/1058] better buttons
---
lnbits/extensions/cashu/config.json | 6 +-
lnbits/extensions/cashu/config.json.example | 6 +-
.../cashu/templates/cashu/index.html | 6 +-
.../cashu/templates/cashu/wallet.html | 123 ++++++++++++------
4 files changed, 93 insertions(+), 48 deletions(-)
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
index 99838eb4..af202d43 100644
--- a/lnbits/extensions/cashu/config.json
+++ b/lnbits/extensions/cashu/config.json
@@ -1,7 +1,7 @@
{
- "name": "Cashu Ecash",
+ "name": "Cashu",
"short_description": "Ecash mint and wallet",
- "icon": "approval",
- "contributors": ["arcbtc", "calle", "vlad"],
+ "icon": "account_balance",
+ "contributors": ["calle", "vlad", "arcbtc"],
"hidden": false
}
diff --git a/lnbits/extensions/cashu/config.json.example b/lnbits/extensions/cashu/config.json.example
index e798e2ef..0cb4043d 100644
--- a/lnbits/extensions/cashu/config.json.example
+++ b/lnbits/extensions/cashu/config.json.example
@@ -1,7 +1,7 @@
{
- "name": "Cashu Ecash",
+ "name": "Cashu",
"short_description": "Ecash mints with LN peg in/out",
- "icon": "approval",
- "contributors": ["arcbtc", "calle"],
+ "icon": "account_balance",
+ "contributors": ["calle", "vlad", "arcbtc"],
"hidden": true
}
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html
index 20ed567c..f54b39b1 100644
--- a/lnbits/extensions/cashu/templates/cashu/index.html
+++ b/lnbits/extensions/cashu/templates/cashu/index.html
@@ -46,12 +46,12 @@
unelevated
dense
size="xs"
- icon="launch"
+ icon="account_balance_wallet"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
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"
- >Shareable wallet page Shareable wallet
Create invoice
+ >Receive invoice
@@ -30,8 +31,9 @@ page_container %}
-
+
+
+
+
-
+ > -->
-
+
-
+
@@ -371,8 +376,9 @@ page_container %}
color="primary"
:disable="payInvoiceData.data.request == ''"
type="submit"
- >ReadContinue
+
Cancel
@@ -595,7 +601,7 @@ page_container %}
-
+
@@ -885,20 +891,6 @@ page_container %}
return this.payments.findIndex(payment => payment.pending) !== -1
},
- tokenList: function () {
- const x = this.proofs
- .map(t => t.amount)
- .reduce((acc, amount) => {
- acc[amount] = acc[amount] + amount || 1
- return acc
- }, {})
- return Object.keys(x).map(k => ({
- value: k,
- count: x[k],
- sum: k * x[k]
- }))
- },
-
balance: function () {
return this.proofs
.map(t => t)
@@ -931,7 +923,7 @@ page_container %}
sum: k * x[k]
}))
},
-
+
paymentTableRowKey: function (row) {
return row.payment_hash + row.amount
},
@@ -1376,7 +1368,7 @@ page_container %}
sumProofs: function (proofs) {
return proofs.reduce((s, t) => (s += t.amount), 0)
},
- splitToSend: async function (proofs, amount) {
+ splitToSend: async function (proofs, amount, invlalidate = false) {
try {
const spendableProofs = proofs.filter(p => !p.reserved)
if (this.sumProofs(spendableProofs) < amount) {
@@ -1395,11 +1387,13 @@ page_container %}
this.proofs[i].reserved = true
}
}
+ if (invlalidate) {
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
+ this.storeProofs()
+ }
- // delete tokens from db
- this.proofs = fristProofs
- // add new fristProofs, scndProofs to this.proofs
- this.storeProofs()
return {fristProofs, scndProofs}
} catch (error) {
console.error(error)
@@ -1508,7 +1502,8 @@ page_container %}
// keep firstProofs, send scndProofs
let {fristProofs, scndProofs} = await this.splitToSend(
this.proofs,
- this.sendData.amount
+ this.sendData.amount,
+ true
)
this.sendData.tokens = ''
this.sendData.tokensBase64 = ''
@@ -1516,11 +1511,39 @@ page_container %}
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
},
-
+ checkFees: async function (payment_request) {
+ const payload = {
+ pr: payment_request
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/checkfees`,
+ '',
+ payload
+ )
+ console.log('#### checkFees', payment_request, data.fee)
+ return data.fee
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
melt: async function () {
// todo: get fees from server and add to inputs
console.log('#### pay lightning')
- const amount = this.payInvoiceData.invoice.sat
+ const amount_invoice = this.payInvoiceData.invoice.sat
+ const amount =
+ amount_invoice +
+ (await this.checkFees(this.payInvoiceData.data.request))
+ console.log(
+ '#### amount invoice',
+ amount_invoice,
+ 'amount with fees',
+ amount
+ )
// if (amount > balance()) {
// LNbits.utils.notifyApiError('Balance too low')
// return
@@ -1547,8 +1570,30 @@ page_container %}
type: 'positive',
message: 'Invoice paid'
})
- this.payInvoiceData.invoice = null
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
+ this.storeProofs()
+ console.log({
+ amount: -amount,
+ bolt11: this.payInvoiceData.data.request,
+ hash: this.payInvoiceData.data.hash,
+ memo: this.payInvoiceData.data.memo,
+ })
+ this.invoicesCashu.push({
+ amount: -amount,
+ bolt11: this.payInvoiceData.data.request,
+ hash: this.payInvoiceData.data.hash,
+ memo: this.payInvoiceData.data.memo,
+ date: currentDateStr(),
+ status: 'paid'
+ })
+ this.storeinvoicesCashu()
+ this.tab = 'invoices'
+
+ this.payInvoiceData.invoice = false
this.payInvoiceData.show = false
+
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
@@ -1558,7 +1603,7 @@ page_container %}
recheckPendingInvoices: async function () {
for (const invoice of this.invoicesCashu) {
- if (invoice.status === 'pending') {
+ if (invoice.status === 'pending' && invoice.sat > 0) {
this.recheckInvoice(invoice.hash, false)
}
}
From a634e51ed496ef0e64eddd38289f0ab290b6bea6 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 05:41:31 +0100
Subject: [PATCH 0310/1058] title
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0debaeb9..b973d4b0 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1,5 +1,4 @@
-{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu
-wallet {% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block
+{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu {% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block
page_container %}
From 06e797b69d9bdf207ba3e1e71dffec78f39cd870 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 05:56:40 +0100
Subject: [PATCH 0311/1058] buttons outline
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index b973d4b0..3e69260d 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -54,6 +54,7 @@ page_container %}
color="primary"
class="full-width"
@click="showReceiveTokensDialog"
+ outline
>Receive Tokens
@@ -66,6 +67,7 @@ page_container %}
color="primary"
class="full-width"
@click="showSendTokensDialog"
+ outline
>
Send Tokens
From e86d8719c3a2fe5f9d9d66312202d7130e38ddf8 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 05:59:29 +0100
Subject: [PATCH 0312/1058] outline
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3e69260d..43040342 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -377,6 +377,7 @@ page_container %}
color="primary"
:disable="payInvoiceData.data.request == ''"
type="submit"
+ outline
>Continue
@@ -550,7 +551,6 @@ page_container %}
v-if="!sendData.tokens"
:disable="sendData.amount == null || sendData.amount <= 0"
@click="sendTokens"
- outline
color="primary"
type="submit"
>Send Tokens
Date: Sat, 5 Nov 2022 06:36:32 +0100
Subject: [PATCH 0313/1058] disclaimer
---
.../cashu/templates/cashu/wallet.html | 95 +++++--------------
1 file changed, 26 insertions(+), 69 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 43040342..eb42a06b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -181,8 +181,18 @@ page_container %}
+
+
+
-
+
Warning
- BOOKMARK THIS PAGE! If only mobile you can also click the 3 dots
- and "Save to homescreen"/"Install app" !
+ Bookmark this page!
- Ecash is a bearer asset, meaning you have the funds saved on this
- page, losing the page without exporting the page will mean you will
- lose the funds.
+ Ecash is a bearer asset, meaning losing access to this wallet will mean you will
+ lose the funds. This wallet stores tokens in its database. If you lose the link or request
+ your data, you will lose your tokens. Bookmark this page or press the
+ hamburger icon and add this page to your home screen.
+
+
+ This service is in BETA, and we hold no responsibility for people losing
+ access to funds. Use at your own risk!
- Copy wallet URL
- Copy wallet URL
+ I understand
@@ -489,14 +501,6 @@ page_container %}
color="primary"
>Copy invoice
-
Create Invoice
@@ -578,7 +582,7 @@ page_container %}
- Paste tokens please
+ Paste Cashu tokens
-
@@ -967,6 +921,9 @@ page_container %}
this.payInvoiceData.camera.show = false
this.focusInput('pasteInput')
},
+ showDisclaimerDialog: function() {
+ this.disclaimerDialog.show = true
+ },
closeReceiveDialog: function () {
setTimeout(() => {
From 6993a4094fc8c3ddd30ebac4aeebed30cbeee834 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 06:39:13 +0100
Subject: [PATCH 0314/1058] warning
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index eb42a06b..c38d0165 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -182,14 +182,14 @@ page_container %}
-
+ @click="showDisclaimerDialog"> Warning
From 2e680705ccd9d03545609180429e6f684a88b25c Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 06:40:12 +0100
Subject: [PATCH 0315/1058] warning
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 1 -
1 file changed, 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index c38d0165..963ac7c4 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -183,7 +183,6 @@ page_container %}
Date: Sat, 5 Nov 2022 06:42:44 +0100
Subject: [PATCH 0316/1058] rows
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 963ac7c4..8cffd248 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -187,7 +187,6 @@ page_container %}
rectangle
color="warning"
outline
- class="full-width"
@click="showDisclaimerDialog"> Warning
@@ -778,7 +777,7 @@ page_container %}
pagination: {
sortBy: 'date',
descending: true,
- rowsPerPage: 10
+ rowsPerPage: 5
},
filter: null
},
@@ -815,7 +814,7 @@ page_container %}
// }
],
pagination: {
- rowsPerPage: 10
+ rowsPerPage: 5
},
filter: null
},
From 6b1d2cc47d610e34515a9dcc6b95cab0c44664c4 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 06:52:31 +0100
Subject: [PATCH 0317/1058] color
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 8cffd248..0832473b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -54,7 +54,6 @@ page_container %}
color="primary"
class="full-width"
@click="showReceiveTokensDialog"
- outline
>Receive Tokens
@@ -67,7 +66,6 @@ page_container %}
color="primary"
class="full-width"
@click="showSendTokensDialog"
- outline
>
Send Tokens
@@ -499,7 +497,7 @@ page_container %}
color="primary"
>Copy invoice
-
Create Invoice
- Paste Cashu tokens
+ Receive Cashu tokens
- Receive Tokens
Date: Sat, 5 Nov 2022 07:02:59 +0100
Subject: [PATCH 0318/1058] check token id
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 8 ++++----
lnbits/extensions/cashu/views_api.py | 9 +++++++++
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0832473b..d8763cbc 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -119,7 +119,7 @@ page_container %}
:data="invoicesCashu"
:columns="invoicesTable.columns"
:pagination.sync="invoicesTable.pagination"
- no-data-label="No invoices made yet"
+ no-data-label="There are no invoices here yet"
:filter="invoicesTable.filter"
>
{% raw %}
@@ -140,12 +140,12 @@ page_container %}
class="q-mr-md cursor-pointer"
@click="recheckInvoice(props.row.hash)"
>
- Recheck
+ Check
-
-
+ Received
+ Paid
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index 806347cb..5f7e9310 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -345,6 +345,15 @@ async def split(
status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist."
)
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
outputs = payload.outputs.blinded_messages
assert outputs, Exception("no outputs provided.")
From aacf7a61b520e3933d45bd5a3bdf35585336ecef Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 07:30:09 +0100
Subject: [PATCH 0319/1058] qtab
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index d8763cbc..722c5e73 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -190,7 +190,7 @@ page_container %}
-
-
From 6a2cf9a99c81f3f7ef918c4256710ffc60ac2cbd Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 07:31:55 +0100
Subject: [PATCH 0320/1058] qtab
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 722c5e73..8adf8fce 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -190,7 +190,7 @@ page_container %}
Date: Sat, 5 Nov 2022 07:42:39 +0100
Subject: [PATCH 0321/1058] vibrate
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 8adf8fce..fa185687 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1158,6 +1158,7 @@ page_container %}
clearInterval(this.invoiceCheckListener)
this.invoiceData.bolt11 = ''
this.showInvoiceDetails = false
+ navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
@@ -1437,6 +1438,12 @@ page_container %}
const proofs = JSON.parse(tokensJson)
const amount = proofs.reduce((s, t) => (s += t.amount), 0)
let {fristProofs, scndProofs} = await this.split(proofs, amount)
+ navigator.vibrate(200)
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Tokens received'
+ })
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
@@ -1457,6 +1464,7 @@ page_container %}
this.sendData.tokens = scndProofs
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
+ navigator.vibrate(200)
},
checkFees: async function (payment_request) {
const payload = {
@@ -1512,6 +1520,7 @@ page_container %}
'',
payload
)
+ navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
From e8b7835c0a7e1bff6c4c60c9487df28dd3a56f02 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 16:54:40 +0100
Subject: [PATCH 0322/1058] multiple mints
---
.../cashu/templates/cashu/wallet.html | 69 ++++++++++---------
1 file changed, 38 insertions(+), 31 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index fa185687..e66e54f5 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1565,6 +1565,15 @@ page_container %}
}
},
+ fetchMintKeys: async function () {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/${this.mintId}/keys`
+ )
+ this.keys = data
+ localStorage.setItem(this.mintKey(this.mintId, 'keys'), JSON.stringify(data))
+ },
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1625,34 +1634,32 @@ page_container %}
this.$q.notify({
timeout: 5000,
type: 'warning',
- message: 'Cannot decode invoice',
+ message: 'Could not decode invoice',
caption: error + ''
})
throw error
}
},
- fetchMintKeys: async function () {
- const {data} = await LNbits.api.request(
- 'GET',
- `/cashu/api/v1/${this.mintId}/keys`
- )
- this.keys = data
- localStorage.setItem('cashu.keys', JSON.stringify(data))
- },
-
storeinvoicesCashu: function () {
localStorage.setItem(
- 'cashu.invoicesCashu',
+ this.mintKey(this.mintId, 'invoicesCashu'),
JSON.stringify(this.invoicesCashu)
)
},
storeProofs: function () {
localStorage.setItem(
- 'cashu.proofs',
+ this.mintKey(this.mintId, 'proofs'),
JSON.stringify(this.proofs, bigIntStringify)
)
- }
+ },
+
+ mintKey: function(mintId, key) {
+ // returns a key for the local storage
+ // depending on the current mint
+ return "cashu." + mintId + "." + key
+ },
+
},
watch: {
payments: function () {
@@ -1663,20 +1670,6 @@ page_container %}
created: function () {
let params = new URL(document.location).searchParams
- // get ticker
- if (
- !params.get('tsh') &&
- !this.$q.localStorage.getItem('cashu.tickershort')
- ) {
- 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')
- } else if (this.$q.localStorage.getItem('cashu.tickershort')) {
- this.tickershort = this.$q.localStorage.getItem('cashu.tickershort')
- }
-
// get mint
if (params.get('mint_id')) {
this.mintId = params.get('mint_id')
@@ -1693,12 +1686,26 @@ page_container %}
// get name
if (params.get('mint_name')) {
this.mintName = params.get('mint_name')
- this.$q.localStorage.set('cashu.mintName', params.get('mint_name'))
+ this.$q.localStorage.set(this.mintKey(this.mintId, 'mintName'), this.mintName)
} else if (this.$q.localStorage.getItem('cashu.name')) {
this.mintName = this.$q.localStorage.getItem('cashu.name')
}
- const keysJson = localStorage.getItem('cashu.keys')
+ // get ticker
+ if (
+ !params.get('tsh') &&
+ !this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
+ ) {
+ this.$q.localStorage.set(this.mintKey(this.mintId, 'tickershort'), 'sats')
+ this.tickershort = 'sats'
+ } else if (params.get('tsh')) {
+ this.$q.localStorage.set(this.mintKey(this.mintId, 'tickershort'), params.get('tsh'))
+ this.tickershort = params.get('tsh')
+ } else if (this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))) {
+ this.tickershort = this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
+ }
+
+ const keysJson = localStorage.getItem(this.mintKey(this.mintId, 'keys'))
if (!keysJson) {
this.fetchMintKeys()
} else {
@@ -1706,9 +1713,9 @@ page_container %}
}
this.invoicesCashu = JSON.parse(
- localStorage.getItem('cashu.invoicesCashu') || '[]'
+ localStorage.getItem(this.mintKey(this.mintId, 'invoicesCashu')) || '[]'
)
- this.proofs = JSON.parse(localStorage.getItem('cashu.proofs') || '[]')
+ this.proofs = JSON.parse(localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]')
console.log('### invoicesCashu', this.invoicesCashu)
console.table('### tokens', this.proofs)
console.log('#### this.mintId', this.mintId)
From c697e8ad5609f1b49b8ca6f8d24dcb344e6e056b Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 17:18:39 +0100
Subject: [PATCH 0323/1058] apple icon
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index e66e54f5..68bf1a16 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1,3 +1,7 @@
+
+
+
+
{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu {% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block
page_container %}
From 888ececc64262b11e3f6390a667e4fc2aa5c3358 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 17:50:35 +0100
Subject: [PATCH 0324/1058] fix chrome
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 11 ++++-------
lnbits/extensions/cashu/views.py | 7 +++++++
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 68bf1a16..0dc5bcf1 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1,9 +1,6 @@
-
-
-
-
-{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu {% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block
-page_container %}
+{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu {% endraw %} {% endblock %}
+{% block footer %}{% endblock %}
+{% block page_container %}
@@ -1321,6 +1318,7 @@ page_container %}
return proofs.reduce((s, t) => (s += t.amount), 0)
},
splitToSend: async function (proofs, amount, invlalidate = false) {
+ // splits proofs so the user can keep firstProofs, send scndProofs
try {
const spendableProofs = proofs.filter(p => !p.reserved)
if (this.sumProofs(spendableProofs) < amount) {
@@ -1330,7 +1328,6 @@ page_container %}
spendableProofs,
amount
)
- // keep firstProofs, send scndProofs
// set scndProofs in this.proofs as reserved
const usedSecrets = proofs.map(p => p.secret)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index ca05d83e..f0a02438 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -86,10 +86,17 @@ async def manifest(cashu_id: str):
{
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"sizes": "96x96",
+ "type": "image/png",
+ },
+ {
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/180x180.png",
+ "sizes": "180x180",
+ "type": "image/png",
},
{
"src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
"sizes": "512x512",
+ "type": "image/png",
},
],
}
From 66d7d7969f4b9b74cff690c0455d3e783fc76d07 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 17:58:07 +0100
Subject: [PATCH 0325/1058] refactor wallet js
---
lnbits/extensions/cashu/static/js/wallet.js | 1104 +++++++++++++++++
.../cashu/templates/cashu/wallet.html | 1102 +---------------
2 files changed, 1105 insertions(+), 1101 deletions(-)
create mode 100644 lnbits/extensions/cashu/static/js/wallet.js
diff --git a/lnbits/extensions/cashu/static/js/wallet.js b/lnbits/extensions/cashu/static/js/wallet.js
new file mode 100644
index 00000000..832b075d
--- /dev/null
+++ b/lnbits/extensions/cashu/static/js/wallet.js
@@ -0,0 +1,1104 @@
+var currentDateStr = function () {
+ return Quasar.utils.date.formatDate(new Date(), 'YYYY-MM-DD HH:mm')
+}
+var mapMint = function (obj) {
+ obj.date = Quasar.utils.date.formatDate(
+ new Date(obj.time * 1000),
+ 'YYYY-MM-DD HH:mm'
+ )
+ obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.amount)
+ obj.cashu = ['/cashu/', obj.id].join('')
+ return obj
+}
+
+Vue.component(VueQrcode.name, VueQrcode)
+
+new Vue({
+ el: '#vue',
+ mixins: [windowMixin],
+ data: function () {
+ return {
+ tickershort: '',
+ name: '',
+
+ mintId: '',
+ mintName: '',
+ keys: '',
+ invoicesCashu: [],
+ invoiceData: {
+ amount: 0,
+ memo: '',
+ bolt11: '',
+ hash: ''
+ },
+ invoiceCheckListener: () => {},
+ payInvoiceData: {
+ // invoice: '',
+ bolt11: '',
+ // camera: {
+ // show: false,
+ // camera: 'auto'
+ // }
+ show: false,
+ invoice: null,
+ lnurlpay: null,
+ lnurlauth: null,
+ data: {
+ request: '',
+ amount: 0,
+ comment: ''
+ },
+ paymentChecker: null,
+ camera: {
+ show: false,
+ camera: 'auto'
+ }
+ },
+ sendData: {
+ amount: 0,
+ memo: '',
+ tokens: '',
+ tokensBase64: ''
+ },
+ receiveData: {
+ tokensBase64: ''
+ },
+ showInvoiceDetails: false,
+ showPayInvoice: false,
+ showSendTokens: false,
+ showReceiveTokens: false,
+ promises: [],
+ tokens: [],
+ tab: 'tokens',
+
+ receive: {
+ show: false,
+ status: 'pending',
+ paymentReq: null,
+ paymentHash: null,
+ minMax: [0, 2100000000000000],
+ lnurl: null,
+ units: ['sat'],
+ unit: 'sat',
+ data: {
+ amount: null,
+ memo: ''
+ }
+ },
+ parse: {
+ show: false,
+ invoice: null,
+ lnurlpay: null,
+ lnurlauth: null,
+ data: {
+ request: '',
+ amount: 0,
+ comment: ''
+ },
+ paymentChecker: null,
+ camera: {
+ show: false,
+ camera: 'auto'
+ }
+ },
+ payments: [],
+ invoicesTable: {
+ columns: [
+ {
+ name: 'status',
+ align: 'left',
+ label: '',
+ field: 'status'
+ },
+ {
+ name: 'amount',
+ align: 'left',
+ label: 'Amount',
+ field: 'amount'
+ },
+ {
+ name: 'memo',
+ align: 'left',
+ label: 'Memo',
+ field: 'memo',
+ sortable: true
+ },
+ {
+ name: 'date',
+ align: 'left',
+ label: 'Date',
+ field: 'date',
+ sortable: true
+ },
+ {
+ name: 'hash',
+ align: 'right',
+ label: 'Hash',
+ field: 'hash',
+ sortable: true
+ }
+ ],
+ pagination: {
+ sortBy: 'date',
+ descending: true,
+ rowsPerPage: 5
+ },
+ filter: null
+ },
+
+ tokensTable: {
+ columns: [
+ {
+ name: 'value',
+ align: 'left',
+ label: 'Value ({{LNBITS_DENOMINATION}})',
+ field: 'value',
+ sortable: true
+ },
+ {
+ name: 'count',
+ align: 'left',
+ label: 'Count',
+ field: 'count',
+ sortable: true
+ },
+ {
+ name: 'sum',
+ align: 'left',
+ label: 'Sum ({{LNBITS_DENOMINATION}})',
+ field: 'sum',
+ sortable: true
+ }
+ // {
+ // name: 'memo',
+ // align: 'left',
+ // label: 'Memo',
+ // field: 'memo',
+ // sortable: true
+ // }
+ ],
+ pagination: {
+ rowsPerPage: 5
+ },
+ filter: null
+ },
+
+ paymentsChart: {
+ show: false
+ },
+ disclaimerDialog: {
+ show: false,
+ location: window.location
+ },
+
+ credit: 0,
+ newName: ''
+ }
+ },
+ computed: {
+ formattedBalance: function () {
+ return this.balance / 100
+ },
+
+ canPay: function () {
+ if (!this.payInvoiceData.invoice) return false
+ return this.payInvoiceData.invoice.sat <= this.balance
+ },
+ pendingPaymentsExist: function () {
+ return this.payments.findIndex(payment => payment.pending) !== -1
+ },
+
+ balance: function () {
+ return this.proofs
+ .map(t => t)
+ .flat()
+ .reduce((sum, el) => (sum += el.amount), 0)
+ }
+ },
+ filters: {
+ msatoshiFormat: function (value) {
+ return LNbits.utils.formatSat(value / 1000)
+ }
+ },
+ methods: {
+ getBalance: function () {
+ return this.proofs
+ .map(t => t)
+ .flat()
+ .reduce((sum, el) => (sum += el.amount), 0)
+ },
+ getTokenList: function () {
+ const x = this.proofs
+ .map(t => t.amount)
+ .reduce((acc, amount) => {
+ acc[amount] = acc[amount] + amount || 1
+ return acc
+ }, {})
+ return Object.keys(x).map(k => ({
+ value: k,
+ count: x[k],
+ sum: k * x[k]
+ }))
+ },
+
+ paymentTableRowKey: function (row) {
+ return row.payment_hash + row.amount
+ },
+ closeCamera: function () {
+ this.payInvoiceData.camera.show = false
+ },
+ showCamera: function () {
+ this.payInvoiceData.camera.show = true
+ },
+ showChart: function () {
+ this.paymentsChart.show = true
+ this.$nextTick(() => {
+ generateChart(this.$refs.canvas, this.payments)
+ })
+ },
+ focusInput(el) {
+ this.$nextTick(() => this.$refs[el].focus())
+ },
+ showReceiveDialog: function () {
+ this.receive.show = true
+ this.receive.status = 'pending'
+ this.receive.paymentReq = null
+ this.receive.paymentHash = null
+ this.receive.data.amount = null
+ this.receive.data.memo = null
+ this.receive.unit = 'sat'
+ this.receive.paymentChecker = null
+ this.receive.minMax = [0, 2100000000000000]
+ this.receive.lnurl = null
+ this.focusInput('setAmount')
+ },
+ showParseDialog: function () {
+ this.payInvoiceData.show = true
+ this.payInvoiceData.invoice = null
+ this.payInvoiceData.lnurlpay = null
+ this.payInvoiceData.lnurlauth = null
+ this.payInvoiceData.data.request = ''
+ this.payInvoiceData.data.comment = ''
+ this.payInvoiceData.data.paymentChecker = null
+ this.payInvoiceData.camera.show = false
+ this.focusInput('pasteInput')
+ },
+ showDisclaimerDialog: function () {
+ this.disclaimerDialog.show = true
+ },
+
+ closeReceiveDialog: function () {
+ setTimeout(() => {
+ clearInterval(this.receive.paymentChecker)
+ }, 10000)
+ },
+ closeParseDialog: function () {
+ setTimeout(() => {
+ clearInterval(this.payInvoiceData.paymentChecker)
+ }, 10000)
+ },
+ onPaymentReceived: function (paymentHash) {
+ this.fetchPayments()
+ this.fetchBalance()
+
+ if (this.receive.paymentHash === paymentHash) {
+ this.receive.show = false
+ this.receive.paymentHash = null
+ clearInterval(this.receive.paymentChecker)
+ }
+ },
+ createInvoice: function () {
+ this.receive.status = 'loading'
+ if (LNBITS_DENOMINATION != 'sats') {
+ this.receive.data.amount = this.receive.data.amount * 100
+ }
+ LNbits.api
+ .createInvoice(
+ this.receive.data.amount,
+ this.receive.data.memo,
+ this.receive.unit,
+ this.receive.lnurl && this.receive.lnurl.callback
+ )
+ .then(response => {
+ this.receive.status = 'success'
+ this.receive.paymentReq = response.data.payment_request
+ this.receive.paymentHash = response.data.payment_hash
+
+ if (response.data.lnurl_response !== null) {
+ if (response.data.lnurl_response === false) {
+ response.data.lnurl_response = `Unable to connect`
+ }
+
+ if (typeof response.data.lnurl_response === 'string') {
+ // failure
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
+ caption: response.data.lnurl_response
+ })
+ return
+ } else if (response.data.lnurl_response === true) {
+ // success
+ this.$q.notify({
+ timeout: 5000,
+ message: `Invoice sent to ${this.receive.lnurl.domain}!`,
+ spinner: true
+ })
+ }
+ }
+
+ clearInterval(this.receive.paymentChecker)
+ setTimeout(() => {
+ clearInterval(this.receive.paymentChecker)
+ }, 40000)
+ })
+ .catch(err => {
+ LNbits.utils.notifyApiError(err)
+ this.receive.status = 'pending'
+ })
+ },
+ decodeQR: function (res) {
+ this.payInvoiceData.data.request = res
+ this.decodeRequest()
+ this.payInvoiceData.camera.show = false
+ },
+ decodeRequest: function () {
+ this.payInvoiceData.show = true
+ let req = this.payInvoiceData.data.request.toLowerCase()
+ if (
+ this.payInvoiceData.data.request.toLowerCase().startsWith('lightning:')
+ ) {
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
+ 10
+ )
+ } else if (
+ this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl:')
+ ) {
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
+ 6
+ )
+ } else if (req.indexOf('lightning=lnurl1') !== -1) {
+ this.payInvoiceData.data.request = this.payInvoiceData.data.request
+ .split('lightning=')[1]
+ .split('&')[0]
+ }
+
+ if (
+ this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl1') ||
+ this.payInvoiceData.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
+ ) {
+ return
+ }
+
+ let invoice
+ try {
+ invoice = decode(this.payInvoiceData.data.request)
+ } catch (error) {
+ this.$q.notify({
+ timeout: 3000,
+ type: 'warning',
+ message: error + '.',
+ caption: '400 BAD REQUEST'
+ })
+ this.payInvoiceData.show = false
+ throw error
+ return
+ }
+
+ let cleanInvoice = {
+ msat: invoice.human_readable_part.amount,
+ sat: invoice.human_readable_part.amount / 1000,
+ fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
+ }
+
+ _.each(invoice.data.tags, tag => {
+ if (_.isObject(tag) && _.has(tag, 'description')) {
+ if (tag.description === 'payment_hash') {
+ cleanInvoice.hash = tag.value
+ } else if (tag.description === 'description') {
+ cleanInvoice.description = tag.value
+ } else if (tag.description === 'expiry') {
+ var expireDate = new Date(
+ (invoice.data.time_stamp + tag.value) * 1000
+ )
+ cleanInvoice.expireDate = Quasar.utils.date.formatDate(
+ expireDate,
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ'
+ )
+ cleanInvoice.expired = false // TODO
+ }
+ }
+ })
+
+ this.payInvoiceData.invoice = Object.freeze(cleanInvoice)
+ },
+ payInvoice: function () {
+ let dismissPaymentMsg = this.$q.notify({
+ timeout: 0,
+ message: 'Processing payment...'
+ })
+ },
+ payLnurl: function () {
+ let dismissPaymentMsg = this.$q.notify({
+ timeout: 0,
+ message: 'Processing payment...'
+ })
+ },
+ authLnurl: function () {
+ let dismissAuthMsg = this.$q.notify({
+ timeout: 10,
+ message: 'Performing authentication...'
+ })
+ },
+
+ deleteWallet: function (walletId, user) {
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this wallet?')
+ .onOk(() => {
+ LNbits.href.deleteWallet(walletId, user)
+ })
+ },
+ fetchPayments: function () {
+ return
+ },
+ fetchBalance: function () {},
+ exportCSV: function () {
+ // status is important for export but it is not in paymentsTable
+ // because it is manually added with payment detail link and icons
+ // and would cause duplication in the list
+ let columns = this.paymentsTable.columns
+ columns.unshift({
+ name: 'pending',
+ align: 'left',
+ label: 'Pending',
+ field: 'pending'
+ })
+ LNbits.utils.exportCSV(columns, this.payments)
+ },
+
+ /////////////////////////////////// WALLET ///////////////////////////////////
+ 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) {
+ console.log('##### showInvoiceDialog')
+ this.invoiceData = _.clone(data)
+ this.showInvoiceDetails = true
+ },
+
+ showPayInvoiceDialog: function () {
+ console.log('### showPayInvoiceDialog')
+ this.payInvoiceData.invoice = ''
+ this.payInvoiceData.data.request = ''
+ this.showPayInvoice = true
+ this.payInvoiceData.camera.show = false
+ },
+
+ showSendTokensDialog: function () {
+ this.sendData.tokens = ''
+ this.sendData.tokensBase64 = ''
+ this.sendData.amount = 0
+ this.sendData.memo = ''
+ this.showSendTokens = true
+ },
+
+ showReceiveTokensDialog: function () {
+ this.receiveData.tokensBase64 = ''
+ this.showReceiveTokens = true
+ },
+
+ //////////////////////// MINT //////////////////////////////////////////
+ requestMintButton: async function () {
+ await this.requestMint()
+ console.log('this is your invoice BEFORE')
+ console.log(this.invoiceData)
+ this.invoiceCheckListener = setInterval(async () => {
+ try {
+ console.log('this is your invoice AFTER')
+ console.log(this.invoiceData)
+ await this.recheckInvoice(this.invoiceData.hash, false)
+ clearInterval(this.invoiceCheckListener)
+ this.invoiceData.bolt11 = ''
+ this.showInvoiceDetails = false
+ navigator.vibrate(200)
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Payment received'
+ })
+ } catch (error) {
+ console.log('not paid yet')
+ }
+ }, 3000)
+ },
+
+ requestMint: async function () {
+ // gets an invoice from the mint to get new tokens
+ try {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}`
+ )
+ console.log('### data', data)
+
+ this.invoiceData.bolt11 = data.pr
+ this.invoiceData.hash = data.hash
+ this.invoicesCashu.push({
+ ..._.clone(this.invoiceData),
+ date: currentDateStr(),
+ status: 'pending'
+ })
+ this.storeinvoicesCashu()
+ this.tab = 'invoices'
+ return data
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+ mintApi: async function (amounts, payment_hash, verbose = true) {
+ console.log('### promises', payment_hash)
+ try {
+ let secrets = await this.generateSecrets(amounts)
+ let {blindedMessages, rs} = await this.constructOutputs(
+ amounts,
+ secrets
+ )
+ const promises = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/mint?payment_hash=${payment_hash}`,
+ '',
+ {
+ blinded_messages: blindedMessages
+ }
+ )
+ console.log('### promises data', promises.data)
+ let proofs = await this.constructProofs(promises.data, secrets, rs)
+ return proofs
+ } catch (error) {
+ console.error(error)
+ if (verbose) {
+ LNbits.utils.notifyApiError(error)
+ }
+ throw error
+ }
+ },
+ mint: async function (amount, payment_hash, verbose = true) {
+ try {
+ const split = splitAmount(amount)
+ const proofs = await this.mintApi(split, payment_hash, verbose)
+ if (!proofs.length) {
+ throw 'could not mint'
+ }
+ this.proofs = this.proofs.concat(proofs)
+ this.storeProofs()
+ await this.setInvoicePaid(payment_hash)
+ return proofs
+ } catch (error) {
+ console.error(error)
+ if (verbose) {
+ LNbits.utils.notifyApiError(error)
+ }
+ throw error
+ }
+ },
+ setInvoicePaid: async function (payment_hash) {
+ const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
+ invoice.status = 'paid'
+ this.storeinvoicesCashu()
+ },
+ recheckInvoice: async function (payment_hash, verbose = true) {
+ console.log('### recheckInvoice.hash', payment_hash)
+ const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
+ try {
+ proofs = await this.mint(invoice.amount, invoice.hash, verbose)
+ return proofs
+ } catch (error) {
+ console.log('Invoice still pending')
+ throw error
+ }
+ },
+
+ generateSecrets: async function (amounts) {
+ const secrets = []
+ for (let i = 0; i < amounts.length; i++) {
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ secrets.push(secret)
+ }
+ return secrets
+ },
+
+ constructOutputs: async function (amounts, secrets) {
+ const blindedMessages = []
+ const rs = []
+ for (let i = 0; i < amounts.length; i++) {
+ const {B_, r} = await step1Alice(secrets[i])
+ blindedMessages.push({amount: amounts[i], B_: B_})
+ rs.push(r)
+ }
+ return {
+ blindedMessages,
+ rs
+ }
+ },
+
+ constructProofs: function (promises, secrets, rs) {
+ const proofs = []
+ for (let i = 0; i < promises.length; i++) {
+ const encodedSecret = uint8ToBase64.encode(secrets[i])
+ let {id, amount, C, secret} = this.promiseToProof(
+ promises[i].id,
+ promises[i].amount,
+ promises[i]['C_'],
+ encodedSecret,
+ rs[i]
+ )
+ proofs.push({id, amount, C, secret})
+ }
+ return proofs
+ },
+
+ promiseToProof: function (id, amount, C_hex, secret, r) {
+ const C_ = nobleSecp256k1.Point.fromHex(C_hex)
+ const A = this.keys[amount]
+ const C = step3Alice(
+ C_,
+ nobleSecp256k1.utils.hexToBytes(r),
+ nobleSecp256k1.Point.fromHex(A)
+ )
+ return {
+ id,
+ amount,
+ C: C.toHex(true),
+ secret
+ }
+ },
+
+ sumProofs: function (proofs) {
+ return proofs.reduce((s, t) => (s += t.amount), 0)
+ },
+ splitToSend: async function (proofs, amount, invlalidate = false) {
+ // splits proofs so the user can keep firstProofs, send scndProofs
+ try {
+ const spendableProofs = proofs.filter(p => !p.reserved)
+ if (this.sumProofs(spendableProofs) < amount) {
+ throw new Error('balance too low.')
+ }
+ let {fristProofs, scndProofs} = await this.split(
+ spendableProofs,
+ amount
+ )
+
+ // set scndProofs in this.proofs as reserved
+ const usedSecrets = proofs.map(p => p.secret)
+ for (let i = 0; i < this.proofs.length; i++) {
+ if (usedSecrets.includes(this.proofs[i].secret)) {
+ this.proofs[i].reserved = true
+ }
+ }
+ if (invlalidate) {
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
+ this.storeProofs()
+ }
+
+ return {fristProofs, scndProofs}
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+
+ split: async function (proofs, amount) {
+ try {
+ if (proofs.length == 0) {
+ throw new Error('no proofs provided.')
+ }
+ let {fristProofs, scndProofs} = await this.splitApi(proofs, amount)
+ // delete proofs from this.proofs
+ const usedSecrets = proofs.map(p => p.secret)
+ this.proofs = this.proofs.filter(p => !usedSecrets.includes(p.secret))
+ // add new fristProofs, scndProofs to this.proofs
+ this.proofs = this.proofs.concat(fristProofs).concat(scndProofs)
+ this.storeProofs()
+ return {fristProofs, scndProofs}
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+ splitApi: async function (proofs, amount) {
+ try {
+ const total = this.sumProofs(proofs)
+ const frst_amount = total - amount
+ const scnd_amount = amount
+ const frst_amounts = splitAmount(frst_amount)
+ const scnd_amounts = splitAmount(scnd_amount)
+ const amounts = _.clone(frst_amounts)
+ amounts.push(...scnd_amounts)
+ let secrets = await this.generateSecrets(amounts)
+ if (secrets.length != amounts.length) {
+ throw new Error('number of secrets does not match number of outputs.')
+ }
+ let {blindedMessages, rs} = await this.constructOutputs(
+ amounts,
+ secrets
+ )
+ const payload = {
+ amount,
+ proofs,
+ outputs: {
+ blinded_messages: blindedMessages
+ }
+ }
+
+ console.log('payload', JSON.stringify(payload))
+
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/split`,
+ '',
+ payload
+ )
+ const frst_rs = rs.slice(0, frst_amounts.length)
+ const frst_secrets = secrets.slice(0, frst_amounts.length)
+ const scnd_rs = rs.slice(frst_amounts.length)
+ const scnd_secrets = secrets.slice(frst_amounts.length)
+ const fristProofs = this.constructProofs(
+ data.fst,
+ frst_secrets,
+ frst_rs
+ )
+ const scndProofs = this.constructProofs(data.snd, scnd_secrets, scnd_rs)
+
+ return {fristProofs, scndProofs}
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+
+ redeem: async function () {
+ this.showReceiveTokens = false
+ console.log('### receive tokens', this.receiveData.tokensBase64)
+ try {
+ if (this.receiveData.tokensBase64.length == 0) {
+ throw new Error('no tokens provided.')
+ }
+ const tokensJson = atob(this.receiveData.tokensBase64)
+ const proofs = JSON.parse(tokensJson)
+ const amount = proofs.reduce((s, t) => (s += t.amount), 0)
+ let {fristProofs, scndProofs} = await this.split(proofs, amount)
+ // HACK: we need to do this so the balance updates
+ this.proofs = this.proofs.concat([])
+ navigator.vibrate(200)
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Tokens received'
+ })
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ // }
+ },
+
+ sendTokens: async function () {
+ // keep firstProofs, send scndProofs
+ let {fristProofs, scndProofs} = await this.splitToSend(
+ this.proofs,
+ this.sendData.amount,
+ true
+ )
+ this.sendData.tokens = ''
+ this.sendData.tokensBase64 = ''
+ this.sendData.tokens = scndProofs
+ console.log('### this.sendData.tokens', this.sendData.tokens)
+ this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
+ navigator.vibrate(200)
+ },
+ checkFees: async function (payment_request) {
+ const payload = {
+ pr: payment_request
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/checkfees`,
+ '',
+ payload
+ )
+ console.log('#### checkFees', payment_request, data.fee)
+ return data.fee
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+ melt: async function () {
+ // todo: get fees from server and add to inputs
+ console.log('#### pay lightning')
+ const amount_invoice = this.payInvoiceData.invoice.sat
+ const amount =
+ amount_invoice +
+ (await this.checkFees(this.payInvoiceData.data.request))
+ console.log(
+ '#### amount invoice',
+ amount_invoice,
+ 'amount with fees',
+ amount
+ )
+ // if (amount > balance()) {
+ // LNbits.utils.notifyApiError('Balance too low')
+ // return
+ // }
+ let {fristProofs, scndProofs} = await this.splitToSend(
+ this.proofs,
+ amount
+ )
+ const payload = {
+ proofs: scndProofs.flat(),
+ amount,
+ invoice: this.payInvoiceData.data.request
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/melt`,
+ '',
+ payload
+ )
+ navigator.vibrate(200)
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Invoice paid'
+ })
+ // delete tokens from db
+ this.proofs = fristProofs
+ // add new fristProofs, scndProofs to this.proofs
+ this.storeProofs()
+ console.log({
+ amount: -amount,
+ bolt11: this.payInvoiceData.data.request,
+ hash: this.payInvoiceData.data.hash,
+ memo: this.payInvoiceData.data.memo
+ })
+ this.invoicesCashu.push({
+ amount: -amount,
+ bolt11: this.payInvoiceData.data.request,
+ hash: this.payInvoiceData.data.hash,
+ memo: this.payInvoiceData.data.memo,
+ date: currentDateStr(),
+ status: 'paid'
+ })
+ this.storeinvoicesCashu()
+ this.tab = 'invoices'
+
+ this.payInvoiceData.invoice = false
+ this.payInvoiceData.show = false
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
+
+ recheckPendingInvoices: async function () {
+ for (const invoice of this.invoicesCashu) {
+ if (invoice.status === 'pending' && invoice.sat > 0) {
+ this.recheckInvoice(invoice.hash, false)
+ }
+ }
+ },
+
+ fetchMintKeys: async function () {
+ const {data} = await LNbits.api.request(
+ 'GET',
+ `/cashu/api/v1/${this.mintId}/keys`
+ )
+ this.keys = data
+ localStorage.setItem(
+ this.mintKey(this.mintId, 'keys'),
+ JSON.stringify(data)
+ )
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ findTokenForAmount: function (amount) {
+ for (const token of this.proofs) {
+ const index = token.promises?.findIndex(p => p.amount === amount)
+ if (index >= 0) {
+ return {
+ promise: token.promises[index],
+ secret: token.secrets[index],
+ r: token.rs[index]
+ }
+ }
+ }
+ },
+
+ checkInvoice: function () {
+ console.log('#### checkInvoice')
+ try {
+ const invoice = decode(this.payInvoiceData.data.request)
+
+ const cleanInvoice = {
+ msat: invoice.human_readable_part.amount,
+ sat: invoice.human_readable_part.amount / 1000,
+ fsat: LNbits.utils.formatSat(
+ invoice.human_readable_part.amount / 1000
+ )
+ }
+
+ _.each(invoice.data.tags, tag => {
+ if (_.isObject(tag) && _.has(tag, 'description')) {
+ if (tag.description === 'payment_hash') {
+ cleanInvoice.hash = tag.value
+ } else if (tag.description === 'description') {
+ cleanInvoice.description = tag.value
+ } else if (tag.description === 'expiry') {
+ var expireDate = new Date(
+ (invoice.data.time_stamp + tag.value) * 1000
+ )
+ cleanInvoice.expireDate = Quasar.utils.date.formatDate(
+ expireDate,
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ'
+ )
+ cleanInvoice.expired = false // TODO
+ }
+ }
+
+ this.payInvoiceData.invoice = cleanInvoice
+ })
+
+ console.log(
+ '#### this.payInvoiceData.invoice',
+ this.payInvoiceData.invoice
+ )
+ } catch (error) {
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: 'Could not decode invoice',
+ caption: error + ''
+ })
+ throw error
+ }
+ },
+
+ storeinvoicesCashu: function () {
+ localStorage.setItem(
+ this.mintKey(this.mintId, 'invoicesCashu'),
+ JSON.stringify(this.invoicesCashu)
+ )
+ },
+ storeProofs: function () {
+ localStorage.setItem(
+ this.mintKey(this.mintId, 'proofs'),
+ JSON.stringify(this.proofs, bigIntStringify)
+ )
+ },
+
+ mintKey: function (mintId, key) {
+ // returns a key for the local storage
+ // depending on the current mint
+ return 'cashu.' + mintId + '.' + key
+ }
+ },
+ watch: {
+ payments: function () {
+ this.balance()
+ }
+ },
+
+ created: function () {
+ let params = new URL(document.location).searchParams
+
+ // get mint
+ if (params.get('mint_id')) {
+ this.mintId = params.get('mint_id')
+ this.$q.localStorage.set('cashu.mint', params.get('mint_id'))
+ } else if (this.$q.localStorage.getItem('cashu.mint')) {
+ this.mintId = this.$q.localStorage.getItem('cashu.mint')
+ } else {
+ this.$q.notify({
+ color: 'red',
+ message: 'No mint set!'
+ })
+ }
+
+ // get name
+ if (params.get('mint_name')) {
+ this.mintName = params.get('mint_name')
+ this.$q.localStorage.set(
+ this.mintKey(this.mintId, 'mintName'),
+ this.mintName
+ )
+ } else if (this.$q.localStorage.getItem('cashu.name')) {
+ this.mintName = this.$q.localStorage.getItem('cashu.name')
+ }
+
+ // get ticker
+ if (
+ !params.get('tsh') &&
+ !this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
+ ) {
+ this.$q.localStorage.set(this.mintKey(this.mintId, 'tickershort'), 'sats')
+ this.tickershort = 'sats'
+ } else if (params.get('tsh')) {
+ this.$q.localStorage.set(
+ this.mintKey(this.mintId, 'tickershort'),
+ params.get('tsh')
+ )
+ this.tickershort = params.get('tsh')
+ } else if (
+ this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
+ ) {
+ this.tickershort = this.$q.localStorage.getItem(
+ this.mintKey(this.mintId, 'tickershort')
+ )
+ }
+
+ const keysJson = localStorage.getItem(this.mintKey(this.mintId, 'keys'))
+ if (!keysJson) {
+ this.fetchMintKeys()
+ } else {
+ this.keys = JSON.parse(keysJson)
+ }
+
+ this.invoicesCashu = JSON.parse(
+ localStorage.getItem(this.mintKey(this.mintId, 'invoicesCashu')) || '[]'
+ )
+ this.proofs = JSON.parse(
+ localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]'
+ )
+ console.log('### invoicesCashu', this.invoicesCashu)
+ console.table('### tokens', this.proofs)
+ console.log('#### this.mintId', this.mintId)
+ console.log('#### this.mintName', this.mintName)
+
+ this.recheckPendingInvoices()
+ }
+})
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0dc5bcf1..7ec99437 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -625,1105 +625,5 @@
-
+
{% endblock %}
From 39db468ed80a41b5609160a6f1429fab82379801 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 18:01:35 +0100
Subject: [PATCH 0326/1058] revert
---
lnbits/extensions/cashu/static/js/wallet.js | 1104 -----------------
.../cashu/templates/cashu/wallet.html | 1104 ++++++++++++++++-
2 files changed, 1103 insertions(+), 1105 deletions(-)
delete mode 100644 lnbits/extensions/cashu/static/js/wallet.js
diff --git a/lnbits/extensions/cashu/static/js/wallet.js b/lnbits/extensions/cashu/static/js/wallet.js
deleted file mode 100644
index 832b075d..00000000
--- a/lnbits/extensions/cashu/static/js/wallet.js
+++ /dev/null
@@ -1,1104 +0,0 @@
-var currentDateStr = function () {
- return Quasar.utils.date.formatDate(new Date(), 'YYYY-MM-DD HH:mm')
-}
-var mapMint = function (obj) {
- obj.date = Quasar.utils.date.formatDate(
- new Date(obj.time * 1000),
- 'YYYY-MM-DD HH:mm'
- )
- obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.amount)
- obj.cashu = ['/cashu/', obj.id].join('')
- return obj
-}
-
-Vue.component(VueQrcode.name, VueQrcode)
-
-new Vue({
- el: '#vue',
- mixins: [windowMixin],
- data: function () {
- return {
- tickershort: '',
- name: '',
-
- mintId: '',
- mintName: '',
- keys: '',
- invoicesCashu: [],
- invoiceData: {
- amount: 0,
- memo: '',
- bolt11: '',
- hash: ''
- },
- invoiceCheckListener: () => {},
- payInvoiceData: {
- // invoice: '',
- bolt11: '',
- // camera: {
- // show: false,
- // camera: 'auto'
- // }
- show: false,
- invoice: null,
- lnurlpay: null,
- lnurlauth: null,
- data: {
- request: '',
- amount: 0,
- comment: ''
- },
- paymentChecker: null,
- camera: {
- show: false,
- camera: 'auto'
- }
- },
- sendData: {
- amount: 0,
- memo: '',
- tokens: '',
- tokensBase64: ''
- },
- receiveData: {
- tokensBase64: ''
- },
- showInvoiceDetails: false,
- showPayInvoice: false,
- showSendTokens: false,
- showReceiveTokens: false,
- promises: [],
- tokens: [],
- tab: 'tokens',
-
- receive: {
- show: false,
- status: 'pending',
- paymentReq: null,
- paymentHash: null,
- minMax: [0, 2100000000000000],
- lnurl: null,
- units: ['sat'],
- unit: 'sat',
- data: {
- amount: null,
- memo: ''
- }
- },
- parse: {
- show: false,
- invoice: null,
- lnurlpay: null,
- lnurlauth: null,
- data: {
- request: '',
- amount: 0,
- comment: ''
- },
- paymentChecker: null,
- camera: {
- show: false,
- camera: 'auto'
- }
- },
- payments: [],
- invoicesTable: {
- columns: [
- {
- name: 'status',
- align: 'left',
- label: '',
- field: 'status'
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount',
- field: 'amount'
- },
- {
- name: 'memo',
- align: 'left',
- label: 'Memo',
- field: 'memo',
- sortable: true
- },
- {
- name: 'date',
- align: 'left',
- label: 'Date',
- field: 'date',
- sortable: true
- },
- {
- name: 'hash',
- align: 'right',
- label: 'Hash',
- field: 'hash',
- sortable: true
- }
- ],
- pagination: {
- sortBy: 'date',
- descending: true,
- rowsPerPage: 5
- },
- filter: null
- },
-
- tokensTable: {
- columns: [
- {
- name: 'value',
- align: 'left',
- label: 'Value ({{LNBITS_DENOMINATION}})',
- field: 'value',
- sortable: true
- },
- {
- name: 'count',
- align: 'left',
- label: 'Count',
- field: 'count',
- sortable: true
- },
- {
- name: 'sum',
- align: 'left',
- label: 'Sum ({{LNBITS_DENOMINATION}})',
- field: 'sum',
- sortable: true
- }
- // {
- // name: 'memo',
- // align: 'left',
- // label: 'Memo',
- // field: 'memo',
- // sortable: true
- // }
- ],
- pagination: {
- rowsPerPage: 5
- },
- filter: null
- },
-
- paymentsChart: {
- show: false
- },
- disclaimerDialog: {
- show: false,
- location: window.location
- },
-
- credit: 0,
- newName: ''
- }
- },
- computed: {
- formattedBalance: function () {
- return this.balance / 100
- },
-
- canPay: function () {
- if (!this.payInvoiceData.invoice) return false
- return this.payInvoiceData.invoice.sat <= this.balance
- },
- pendingPaymentsExist: function () {
- return this.payments.findIndex(payment => payment.pending) !== -1
- },
-
- balance: function () {
- return this.proofs
- .map(t => t)
- .flat()
- .reduce((sum, el) => (sum += el.amount), 0)
- }
- },
- filters: {
- msatoshiFormat: function (value) {
- return LNbits.utils.formatSat(value / 1000)
- }
- },
- methods: {
- getBalance: function () {
- return this.proofs
- .map(t => t)
- .flat()
- .reduce((sum, el) => (sum += el.amount), 0)
- },
- getTokenList: function () {
- const x = this.proofs
- .map(t => t.amount)
- .reduce((acc, amount) => {
- acc[amount] = acc[amount] + amount || 1
- return acc
- }, {})
- return Object.keys(x).map(k => ({
- value: k,
- count: x[k],
- sum: k * x[k]
- }))
- },
-
- paymentTableRowKey: function (row) {
- return row.payment_hash + row.amount
- },
- closeCamera: function () {
- this.payInvoiceData.camera.show = false
- },
- showCamera: function () {
- this.payInvoiceData.camera.show = true
- },
- showChart: function () {
- this.paymentsChart.show = true
- this.$nextTick(() => {
- generateChart(this.$refs.canvas, this.payments)
- })
- },
- focusInput(el) {
- this.$nextTick(() => this.$refs[el].focus())
- },
- showReceiveDialog: function () {
- this.receive.show = true
- this.receive.status = 'pending'
- this.receive.paymentReq = null
- this.receive.paymentHash = null
- this.receive.data.amount = null
- this.receive.data.memo = null
- this.receive.unit = 'sat'
- this.receive.paymentChecker = null
- this.receive.minMax = [0, 2100000000000000]
- this.receive.lnurl = null
- this.focusInput('setAmount')
- },
- showParseDialog: function () {
- this.payInvoiceData.show = true
- this.payInvoiceData.invoice = null
- this.payInvoiceData.lnurlpay = null
- this.payInvoiceData.lnurlauth = null
- this.payInvoiceData.data.request = ''
- this.payInvoiceData.data.comment = ''
- this.payInvoiceData.data.paymentChecker = null
- this.payInvoiceData.camera.show = false
- this.focusInput('pasteInput')
- },
- showDisclaimerDialog: function () {
- this.disclaimerDialog.show = true
- },
-
- closeReceiveDialog: function () {
- setTimeout(() => {
- clearInterval(this.receive.paymentChecker)
- }, 10000)
- },
- closeParseDialog: function () {
- setTimeout(() => {
- clearInterval(this.payInvoiceData.paymentChecker)
- }, 10000)
- },
- onPaymentReceived: function (paymentHash) {
- this.fetchPayments()
- this.fetchBalance()
-
- if (this.receive.paymentHash === paymentHash) {
- this.receive.show = false
- this.receive.paymentHash = null
- clearInterval(this.receive.paymentChecker)
- }
- },
- createInvoice: function () {
- this.receive.status = 'loading'
- if (LNBITS_DENOMINATION != 'sats') {
- this.receive.data.amount = this.receive.data.amount * 100
- }
- LNbits.api
- .createInvoice(
- this.receive.data.amount,
- this.receive.data.memo,
- this.receive.unit,
- this.receive.lnurl && this.receive.lnurl.callback
- )
- .then(response => {
- this.receive.status = 'success'
- this.receive.paymentReq = response.data.payment_request
- this.receive.paymentHash = response.data.payment_hash
-
- if (response.data.lnurl_response !== null) {
- if (response.data.lnurl_response === false) {
- response.data.lnurl_response = `Unable to connect`
- }
-
- if (typeof response.data.lnurl_response === 'string') {
- // failure
- this.$q.notify({
- timeout: 5000,
- type: 'warning',
- message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
- caption: response.data.lnurl_response
- })
- return
- } else if (response.data.lnurl_response === true) {
- // success
- this.$q.notify({
- timeout: 5000,
- message: `Invoice sent to ${this.receive.lnurl.domain}!`,
- spinner: true
- })
- }
- }
-
- clearInterval(this.receive.paymentChecker)
- setTimeout(() => {
- clearInterval(this.receive.paymentChecker)
- }, 40000)
- })
- .catch(err => {
- LNbits.utils.notifyApiError(err)
- this.receive.status = 'pending'
- })
- },
- decodeQR: function (res) {
- this.payInvoiceData.data.request = res
- this.decodeRequest()
- this.payInvoiceData.camera.show = false
- },
- decodeRequest: function () {
- this.payInvoiceData.show = true
- let req = this.payInvoiceData.data.request.toLowerCase()
- if (
- this.payInvoiceData.data.request.toLowerCase().startsWith('lightning:')
- ) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
- 10
- )
- } else if (
- this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl:')
- ) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
- 6
- )
- } else if (req.indexOf('lightning=lnurl1') !== -1) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request
- .split('lightning=')[1]
- .split('&')[0]
- }
-
- if (
- this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl1') ||
- this.payInvoiceData.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
- ) {
- return
- }
-
- let invoice
- try {
- invoice = decode(this.payInvoiceData.data.request)
- } catch (error) {
- this.$q.notify({
- timeout: 3000,
- type: 'warning',
- message: error + '.',
- caption: '400 BAD REQUEST'
- })
- this.payInvoiceData.show = false
- throw error
- return
- }
-
- let cleanInvoice = {
- msat: invoice.human_readable_part.amount,
- sat: invoice.human_readable_part.amount / 1000,
- fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
- }
-
- _.each(invoice.data.tags, tag => {
- if (_.isObject(tag) && _.has(tag, 'description')) {
- if (tag.description === 'payment_hash') {
- cleanInvoice.hash = tag.value
- } else if (tag.description === 'description') {
- cleanInvoice.description = tag.value
- } else if (tag.description === 'expiry') {
- var expireDate = new Date(
- (invoice.data.time_stamp + tag.value) * 1000
- )
- cleanInvoice.expireDate = Quasar.utils.date.formatDate(
- expireDate,
- 'YYYY-MM-DDTHH:mm:ss.SSSZ'
- )
- cleanInvoice.expired = false // TODO
- }
- }
- })
-
- this.payInvoiceData.invoice = Object.freeze(cleanInvoice)
- },
- payInvoice: function () {
- let dismissPaymentMsg = this.$q.notify({
- timeout: 0,
- message: 'Processing payment...'
- })
- },
- payLnurl: function () {
- let dismissPaymentMsg = this.$q.notify({
- timeout: 0,
- message: 'Processing payment...'
- })
- },
- authLnurl: function () {
- let dismissAuthMsg = this.$q.notify({
- timeout: 10,
- message: 'Performing authentication...'
- })
- },
-
- deleteWallet: function (walletId, user) {
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this wallet?')
- .onOk(() => {
- LNbits.href.deleteWallet(walletId, user)
- })
- },
- fetchPayments: function () {
- return
- },
- fetchBalance: function () {},
- exportCSV: function () {
- // status is important for export but it is not in paymentsTable
- // because it is manually added with payment detail link and icons
- // and would cause duplication in the list
- let columns = this.paymentsTable.columns
- columns.unshift({
- name: 'pending',
- align: 'left',
- label: 'Pending',
- field: 'pending'
- })
- LNbits.utils.exportCSV(columns, this.payments)
- },
-
- /////////////////////////////////// WALLET ///////////////////////////////////
- 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) {
- console.log('##### showInvoiceDialog')
- this.invoiceData = _.clone(data)
- this.showInvoiceDetails = true
- },
-
- showPayInvoiceDialog: function () {
- console.log('### showPayInvoiceDialog')
- this.payInvoiceData.invoice = ''
- this.payInvoiceData.data.request = ''
- this.showPayInvoice = true
- this.payInvoiceData.camera.show = false
- },
-
- showSendTokensDialog: function () {
- this.sendData.tokens = ''
- this.sendData.tokensBase64 = ''
- this.sendData.amount = 0
- this.sendData.memo = ''
- this.showSendTokens = true
- },
-
- showReceiveTokensDialog: function () {
- this.receiveData.tokensBase64 = ''
- this.showReceiveTokens = true
- },
-
- //////////////////////// MINT //////////////////////////////////////////
- requestMintButton: async function () {
- await this.requestMint()
- console.log('this is your invoice BEFORE')
- console.log(this.invoiceData)
- this.invoiceCheckListener = setInterval(async () => {
- try {
- console.log('this is your invoice AFTER')
- console.log(this.invoiceData)
- await this.recheckInvoice(this.invoiceData.hash, false)
- clearInterval(this.invoiceCheckListener)
- this.invoiceData.bolt11 = ''
- this.showInvoiceDetails = false
- navigator.vibrate(200)
- this.$q.notify({
- timeout: 5000,
- type: 'positive',
- message: 'Payment received'
- })
- } catch (error) {
- console.log('not paid yet')
- }
- }, 3000)
- },
-
- requestMint: async function () {
- // gets an invoice from the mint to get new tokens
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}`
- )
- console.log('### data', data)
-
- this.invoiceData.bolt11 = data.pr
- this.invoiceData.hash = data.hash
- this.invoicesCashu.push({
- ..._.clone(this.invoiceData),
- date: currentDateStr(),
- status: 'pending'
- })
- this.storeinvoicesCashu()
- this.tab = 'invoices'
- return data
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
- mintApi: async function (amounts, payment_hash, verbose = true) {
- console.log('### promises', payment_hash)
- try {
- let secrets = await this.generateSecrets(amounts)
- let {blindedMessages, rs} = await this.constructOutputs(
- amounts,
- secrets
- )
- const promises = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/mint?payment_hash=${payment_hash}`,
- '',
- {
- blinded_messages: blindedMessages
- }
- )
- console.log('### promises data', promises.data)
- let proofs = await this.constructProofs(promises.data, secrets, rs)
- return proofs
- } catch (error) {
- console.error(error)
- if (verbose) {
- LNbits.utils.notifyApiError(error)
- }
- throw error
- }
- },
- mint: async function (amount, payment_hash, verbose = true) {
- try {
- const split = splitAmount(amount)
- const proofs = await this.mintApi(split, payment_hash, verbose)
- if (!proofs.length) {
- throw 'could not mint'
- }
- this.proofs = this.proofs.concat(proofs)
- this.storeProofs()
- await this.setInvoicePaid(payment_hash)
- return proofs
- } catch (error) {
- console.error(error)
- if (verbose) {
- LNbits.utils.notifyApiError(error)
- }
- throw error
- }
- },
- setInvoicePaid: async function (payment_hash) {
- const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
- invoice.status = 'paid'
- this.storeinvoicesCashu()
- },
- recheckInvoice: async function (payment_hash, verbose = true) {
- console.log('### recheckInvoice.hash', payment_hash)
- const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
- try {
- proofs = await this.mint(invoice.amount, invoice.hash, verbose)
- return proofs
- } catch (error) {
- console.log('Invoice still pending')
- throw error
- }
- },
-
- generateSecrets: async function (amounts) {
- const secrets = []
- for (let i = 0; i < amounts.length; i++) {
- const secret = nobleSecp256k1.utils.randomBytes(32)
- secrets.push(secret)
- }
- return secrets
- },
-
- constructOutputs: async function (amounts, secrets) {
- const blindedMessages = []
- const rs = []
- for (let i = 0; i < amounts.length; i++) {
- const {B_, r} = await step1Alice(secrets[i])
- blindedMessages.push({amount: amounts[i], B_: B_})
- rs.push(r)
- }
- return {
- blindedMessages,
- rs
- }
- },
-
- constructProofs: function (promises, secrets, rs) {
- const proofs = []
- for (let i = 0; i < promises.length; i++) {
- const encodedSecret = uint8ToBase64.encode(secrets[i])
- let {id, amount, C, secret} = this.promiseToProof(
- promises[i].id,
- promises[i].amount,
- promises[i]['C_'],
- encodedSecret,
- rs[i]
- )
- proofs.push({id, amount, C, secret})
- }
- return proofs
- },
-
- promiseToProof: function (id, amount, C_hex, secret, r) {
- const C_ = nobleSecp256k1.Point.fromHex(C_hex)
- const A = this.keys[amount]
- const C = step3Alice(
- C_,
- nobleSecp256k1.utils.hexToBytes(r),
- nobleSecp256k1.Point.fromHex(A)
- )
- return {
- id,
- amount,
- C: C.toHex(true),
- secret
- }
- },
-
- sumProofs: function (proofs) {
- return proofs.reduce((s, t) => (s += t.amount), 0)
- },
- splitToSend: async function (proofs, amount, invlalidate = false) {
- // splits proofs so the user can keep firstProofs, send scndProofs
- try {
- const spendableProofs = proofs.filter(p => !p.reserved)
- if (this.sumProofs(spendableProofs) < amount) {
- throw new Error('balance too low.')
- }
- let {fristProofs, scndProofs} = await this.split(
- spendableProofs,
- amount
- )
-
- // set scndProofs in this.proofs as reserved
- const usedSecrets = proofs.map(p => p.secret)
- for (let i = 0; i < this.proofs.length; i++) {
- if (usedSecrets.includes(this.proofs[i].secret)) {
- this.proofs[i].reserved = true
- }
- }
- if (invlalidate) {
- // delete tokens from db
- this.proofs = fristProofs
- // add new fristProofs, scndProofs to this.proofs
- this.storeProofs()
- }
-
- return {fristProofs, scndProofs}
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
-
- split: async function (proofs, amount) {
- try {
- if (proofs.length == 0) {
- throw new Error('no proofs provided.')
- }
- let {fristProofs, scndProofs} = await this.splitApi(proofs, amount)
- // delete proofs from this.proofs
- const usedSecrets = proofs.map(p => p.secret)
- this.proofs = this.proofs.filter(p => !usedSecrets.includes(p.secret))
- // add new fristProofs, scndProofs to this.proofs
- this.proofs = this.proofs.concat(fristProofs).concat(scndProofs)
- this.storeProofs()
- return {fristProofs, scndProofs}
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
- splitApi: async function (proofs, amount) {
- try {
- const total = this.sumProofs(proofs)
- const frst_amount = total - amount
- const scnd_amount = amount
- const frst_amounts = splitAmount(frst_amount)
- const scnd_amounts = splitAmount(scnd_amount)
- const amounts = _.clone(frst_amounts)
- amounts.push(...scnd_amounts)
- let secrets = await this.generateSecrets(amounts)
- if (secrets.length != amounts.length) {
- throw new Error('number of secrets does not match number of outputs.')
- }
- let {blindedMessages, rs} = await this.constructOutputs(
- amounts,
- secrets
- )
- const payload = {
- amount,
- proofs,
- outputs: {
- blinded_messages: blindedMessages
- }
- }
-
- console.log('payload', JSON.stringify(payload))
-
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/split`,
- '',
- payload
- )
- const frst_rs = rs.slice(0, frst_amounts.length)
- const frst_secrets = secrets.slice(0, frst_amounts.length)
- const scnd_rs = rs.slice(frst_amounts.length)
- const scnd_secrets = secrets.slice(frst_amounts.length)
- const fristProofs = this.constructProofs(
- data.fst,
- frst_secrets,
- frst_rs
- )
- const scndProofs = this.constructProofs(data.snd, scnd_secrets, scnd_rs)
-
- return {fristProofs, scndProofs}
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
-
- redeem: async function () {
- this.showReceiveTokens = false
- console.log('### receive tokens', this.receiveData.tokensBase64)
- try {
- if (this.receiveData.tokensBase64.length == 0) {
- throw new Error('no tokens provided.')
- }
- const tokensJson = atob(this.receiveData.tokensBase64)
- const proofs = JSON.parse(tokensJson)
- const amount = proofs.reduce((s, t) => (s += t.amount), 0)
- let {fristProofs, scndProofs} = await this.split(proofs, amount)
- // HACK: we need to do this so the balance updates
- this.proofs = this.proofs.concat([])
- navigator.vibrate(200)
- this.$q.notify({
- timeout: 5000,
- type: 'positive',
- message: 'Tokens received'
- })
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- // }
- },
-
- sendTokens: async function () {
- // keep firstProofs, send scndProofs
- let {fristProofs, scndProofs} = await this.splitToSend(
- this.proofs,
- this.sendData.amount,
- true
- )
- this.sendData.tokens = ''
- this.sendData.tokensBase64 = ''
- this.sendData.tokens = scndProofs
- console.log('### this.sendData.tokens', this.sendData.tokens)
- this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
- navigator.vibrate(200)
- },
- checkFees: async function (payment_request) {
- const payload = {
- pr: payment_request
- }
- console.log('#### payload', JSON.stringify(payload))
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/checkfees`,
- '',
- payload
- )
- console.log('#### checkFees', payment_request, data.fee)
- return data.fee
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
- melt: async function () {
- // todo: get fees from server and add to inputs
- console.log('#### pay lightning')
- const amount_invoice = this.payInvoiceData.invoice.sat
- const amount =
- amount_invoice +
- (await this.checkFees(this.payInvoiceData.data.request))
- console.log(
- '#### amount invoice',
- amount_invoice,
- 'amount with fees',
- amount
- )
- // if (amount > balance()) {
- // LNbits.utils.notifyApiError('Balance too low')
- // return
- // }
- let {fristProofs, scndProofs} = await this.splitToSend(
- this.proofs,
- amount
- )
- const payload = {
- proofs: scndProofs.flat(),
- amount,
- invoice: this.payInvoiceData.data.request
- }
- console.log('#### payload', JSON.stringify(payload))
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- `/cashu/api/v1/${this.mintId}/melt`,
- '',
- payload
- )
- navigator.vibrate(200)
- this.$q.notify({
- timeout: 5000,
- type: 'positive',
- message: 'Invoice paid'
- })
- // delete tokens from db
- this.proofs = fristProofs
- // add new fristProofs, scndProofs to this.proofs
- this.storeProofs()
- console.log({
- amount: -amount,
- bolt11: this.payInvoiceData.data.request,
- hash: this.payInvoiceData.data.hash,
- memo: this.payInvoiceData.data.memo
- })
- this.invoicesCashu.push({
- amount: -amount,
- bolt11: this.payInvoiceData.data.request,
- hash: this.payInvoiceData.data.hash,
- memo: this.payInvoiceData.data.memo,
- date: currentDateStr(),
- status: 'paid'
- })
- this.storeinvoicesCashu()
- this.tab = 'invoices'
-
- this.payInvoiceData.invoice = false
- this.payInvoiceData.show = false
- } catch (error) {
- console.error(error)
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
-
- recheckPendingInvoices: async function () {
- for (const invoice of this.invoicesCashu) {
- if (invoice.status === 'pending' && invoice.sat > 0) {
- this.recheckInvoice(invoice.hash, false)
- }
- }
- },
-
- fetchMintKeys: async function () {
- const {data} = await LNbits.api.request(
- 'GET',
- `/cashu/api/v1/${this.mintId}/keys`
- )
- this.keys = data
- localStorage.setItem(
- this.mintKey(this.mintId, 'keys'),
- JSON.stringify(data)
- )
- },
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- findTokenForAmount: function (amount) {
- for (const token of this.proofs) {
- const index = token.promises?.findIndex(p => p.amount === amount)
- if (index >= 0) {
- return {
- promise: token.promises[index],
- secret: token.secrets[index],
- r: token.rs[index]
- }
- }
- }
- },
-
- checkInvoice: function () {
- console.log('#### checkInvoice')
- try {
- const invoice = decode(this.payInvoiceData.data.request)
-
- const cleanInvoice = {
- msat: invoice.human_readable_part.amount,
- sat: invoice.human_readable_part.amount / 1000,
- fsat: LNbits.utils.formatSat(
- invoice.human_readable_part.amount / 1000
- )
- }
-
- _.each(invoice.data.tags, tag => {
- if (_.isObject(tag) && _.has(tag, 'description')) {
- if (tag.description === 'payment_hash') {
- cleanInvoice.hash = tag.value
- } else if (tag.description === 'description') {
- cleanInvoice.description = tag.value
- } else if (tag.description === 'expiry') {
- var expireDate = new Date(
- (invoice.data.time_stamp + tag.value) * 1000
- )
- cleanInvoice.expireDate = Quasar.utils.date.formatDate(
- expireDate,
- 'YYYY-MM-DDTHH:mm:ss.SSSZ'
- )
- cleanInvoice.expired = false // TODO
- }
- }
-
- this.payInvoiceData.invoice = cleanInvoice
- })
-
- console.log(
- '#### this.payInvoiceData.invoice',
- this.payInvoiceData.invoice
- )
- } catch (error) {
- this.$q.notify({
- timeout: 5000,
- type: 'warning',
- message: 'Could not decode invoice',
- caption: error + ''
- })
- throw error
- }
- },
-
- storeinvoicesCashu: function () {
- localStorage.setItem(
- this.mintKey(this.mintId, 'invoicesCashu'),
- JSON.stringify(this.invoicesCashu)
- )
- },
- storeProofs: function () {
- localStorage.setItem(
- this.mintKey(this.mintId, 'proofs'),
- JSON.stringify(this.proofs, bigIntStringify)
- )
- },
-
- mintKey: function (mintId, key) {
- // returns a key for the local storage
- // depending on the current mint
- return 'cashu.' + mintId + '.' + key
- }
- },
- watch: {
- payments: function () {
- this.balance()
- }
- },
-
- created: function () {
- let params = new URL(document.location).searchParams
-
- // get mint
- if (params.get('mint_id')) {
- this.mintId = params.get('mint_id')
- this.$q.localStorage.set('cashu.mint', params.get('mint_id'))
- } else if (this.$q.localStorage.getItem('cashu.mint')) {
- this.mintId = this.$q.localStorage.getItem('cashu.mint')
- } else {
- this.$q.notify({
- color: 'red',
- message: 'No mint set!'
- })
- }
-
- // get name
- if (params.get('mint_name')) {
- this.mintName = params.get('mint_name')
- this.$q.localStorage.set(
- this.mintKey(this.mintId, 'mintName'),
- this.mintName
- )
- } else if (this.$q.localStorage.getItem('cashu.name')) {
- this.mintName = this.$q.localStorage.getItem('cashu.name')
- }
-
- // get ticker
- if (
- !params.get('tsh') &&
- !this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
- ) {
- this.$q.localStorage.set(this.mintKey(this.mintId, 'tickershort'), 'sats')
- this.tickershort = 'sats'
- } else if (params.get('tsh')) {
- this.$q.localStorage.set(
- this.mintKey(this.mintId, 'tickershort'),
- params.get('tsh')
- )
- this.tickershort = params.get('tsh')
- } else if (
- this.$q.localStorage.getItem(this.mintKey(this.mintId, 'tickershort'))
- ) {
- this.tickershort = this.$q.localStorage.getItem(
- this.mintKey(this.mintId, 'tickershort')
- )
- }
-
- const keysJson = localStorage.getItem(this.mintKey(this.mintId, 'keys'))
- if (!keysJson) {
- this.fetchMintKeys()
- } else {
- this.keys = JSON.parse(keysJson)
- }
-
- this.invoicesCashu = JSON.parse(
- localStorage.getItem(this.mintKey(this.mintId, 'invoicesCashu')) || '[]'
- )
- this.proofs = JSON.parse(
- localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]'
- )
- console.log('### invoicesCashu', this.invoicesCashu)
- console.table('### tokens', this.proofs)
- console.log('#### this.mintId', this.mintId)
- console.log('#### this.mintName', this.mintName)
-
- this.recheckPendingInvoices()
- }
-})
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 7ec99437..88af72ca 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -625,5 +625,1107 @@
-
+
{% endblock %}
From 2488bb9da393870d838cdfb8712b7c5f2e47dfec Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 19:12:13 +0100
Subject: [PATCH 0327/1058] check token history
---
.../cashu/templates/cashu/wallet.html | 388 +++++++++++++-----
1 file changed, 293 insertions(+), 95 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 88af72ca..6d86ff35 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -72,13 +72,19 @@
>
-
+
+
+
+
+
+
+
+
{{props.row.amount}}
-
+
{{props.row.date}}
@@ -173,10 +182,68 @@
{% endraw %}
+
+
- History
+
+ {% raw %}
+
+
+
+
+
+ Pending
+
+
+ Check
+
+
+
+ Received
+ Paid
+
+
+
+
+
+ {{props.row.amount}}
+
+
+
+ {{props.row.date}}
+
+
+ {{props.row.token}}
+
+
+
+ {% endraw %}
+
+
+
@@ -653,6 +720,7 @@
mintName: '',
keys: '',
invoicesCashu: [],
+ historyTokens: [],
invoiceData: {
amount: 0,
memo: '',
@@ -736,21 +804,23 @@
name: 'status',
align: 'left',
label: '',
- field: 'status'
+ field: 'status',
+ sortable: true
},
{
name: 'amount',
align: 'left',
label: 'Amount',
- field: 'amount'
- },
- {
- name: 'memo',
- align: 'left',
- label: 'Memo',
- field: 'memo',
+ field: 'amount',
sortable: true
},
+ // {
+ // name: 'memo',
+ // align: 'left',
+ // label: 'Memo',
+ // field: 'memo',
+ // sortable: true
+ // },
{
name: 'date',
align: 'left',
@@ -763,7 +833,7 @@
align: 'right',
label: 'Hash',
field: 'hash',
- sortable: true
+ sortable: false
}
],
pagination: {
@@ -811,6 +881,43 @@
filter: null
},
+ historyTable: {
+ columns: [
+ {
+ name: 'status',
+ align: 'left',
+ label: '',
+ field: 'status',
+ sortable: true
+ },
+ {
+ name: 'amount',
+ align: 'left',
+ label: 'Value ({{LNBITS_DENOMINATION}})',
+ field: 'amount',
+ sortable: true
+ },
+ {
+ name: 'date',
+ align: 'left',
+ label: 'Date',
+ field: 'date',
+ sortable: true
+ },
+ {
+ name: 'token',
+ align: 'left',
+ label: 'Token',
+ field: 'token',
+ sortable: false
+ }
+ ],
+ pagination: {
+ rowsPerPage: 5
+ },
+ filter: null
+ },
+
paymentsChart: {
show: false
},
@@ -1147,15 +1254,90 @@
},
//////////////////////// MINT //////////////////////////////////////////
+
+ generateSecrets: async function (amounts) {
+ const secrets = []
+ for (let i = 0; i < amounts.length; i++) {
+ const secret = nobleSecp256k1.utils.randomBytes(32)
+ secrets.push(secret)
+ }
+ return secrets
+ },
+
+ constructOutputs: async function (amounts, secrets) {
+ const blindedMessages = []
+ const rs = []
+ for (let i = 0; i < amounts.length; i++) {
+ const {B_, r} = await step1Alice(secrets[i])
+ blindedMessages.push({amount: amounts[i], B_: B_})
+ rs.push(r)
+ }
+ return {
+ blindedMessages,
+ rs
+ }
+ },
+
+ constructProofs: function (promises, secrets, rs) {
+ const proofs = []
+ for (let i = 0; i < promises.length; i++) {
+ const encodedSecret = uint8ToBase64.encode(secrets[i])
+ let {id, amount, C, secret} = this.promiseToProof(
+ promises[i].id,
+ promises[i].amount,
+ promises[i]['C_'],
+ encodedSecret,
+ rs[i]
+ )
+ proofs.push({id, amount, C, secret})
+ }
+ return proofs
+ },
+
+ promiseToProof: function (id, amount, C_hex, secret, r) {
+ const C_ = nobleSecp256k1.Point.fromHex(C_hex)
+ const A = this.keys[amount]
+ const C = step3Alice(
+ C_,
+ nobleSecp256k1.utils.hexToBytes(r),
+ nobleSecp256k1.Point.fromHex(A)
+ )
+ return {
+ id,
+ amount,
+ C: C.toHex(true),
+ secret
+ }
+ },
+
+ sumProofs: function (proofs) {
+ return proofs.reduce((s, t) => (s += t.amount), 0)
+ },
+
+
+ //////////// API ///////////
+
requestMintButton: async function () {
await this.requestMint()
- console.log('this is your invoice BEFORE')
- console.log(this.invoiceData)
+ console.log("#### request mint", this.invoiceData)
+ let nInterval = 0
this.invoiceCheckListener = setInterval(async () => {
try {
- console.log('this is your invoice AFTER')
+ nInterval += 1
+
+ // exit loop after 5m
+ if (nInterval > 100) {
+ console.log("### stopping invoice check worker")
+ clearInterval(this.invoiceCheckListener)
+ }
+ console.log('### setInterval', nInterval)
console.log(this.invoiceData)
+
+ // this will throw an error if the invoice is pending
await this.recheckInvoice(this.invoiceData.hash, false)
+
+ // only without error (invoice paid) will we reach here
+ console.log("### stopping invoice check worker")
clearInterval(this.invoiceCheckListener)
this.invoiceData.bolt11 = ''
this.showInvoiceDetails = false
@@ -1242,81 +1424,6 @@
throw error
}
},
- setInvoicePaid: async function (payment_hash) {
- const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
- invoice.status = 'paid'
- this.storeinvoicesCashu()
- },
- recheckInvoice: async function (payment_hash, verbose = true) {
- console.log('### recheckInvoice.hash', payment_hash)
- const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
- try {
- proofs = await this.mint(invoice.amount, invoice.hash, verbose)
- return proofs
- } catch (error) {
- console.log('Invoice still pending')
- throw error
- }
- },
-
- generateSecrets: async function (amounts) {
- const secrets = []
- for (let i = 0; i < amounts.length; i++) {
- const secret = nobleSecp256k1.utils.randomBytes(32)
- secrets.push(secret)
- }
- return secrets
- },
-
- constructOutputs: async function (amounts, secrets) {
- const blindedMessages = []
- const rs = []
- for (let i = 0; i < amounts.length; i++) {
- const {B_, r} = await step1Alice(secrets[i])
- blindedMessages.push({amount: amounts[i], B_: B_})
- rs.push(r)
- }
- return {
- blindedMessages,
- rs
- }
- },
-
- constructProofs: function (promises, secrets, rs) {
- const proofs = []
- for (let i = 0; i < promises.length; i++) {
- const encodedSecret = uint8ToBase64.encode(secrets[i])
- let {id, amount, C, secret} = this.promiseToProof(
- promises[i].id,
- promises[i].amount,
- promises[i]['C_'],
- encodedSecret,
- rs[i]
- )
- proofs.push({id, amount, C, secret})
- }
- return proofs
- },
-
- promiseToProof: function (id, amount, C_hex, secret, r) {
- const C_ = nobleSecp256k1.Point.fromHex(C_hex)
- const A = this.keys[amount]
- const C = step3Alice(
- C_,
- nobleSecp256k1.utils.hexToBytes(r),
- nobleSecp256k1.Point.fromHex(A)
- )
- return {
- id,
- amount,
- C: C.toHex(true),
- secret
- }
- },
-
- sumProofs: function (proofs) {
- return proofs.reduce((s, t) => (s += t.amount), 0)
- },
splitToSend: async function (proofs, amount, invlalidate = false) {
// splits proofs so the user can keep firstProofs, send scndProofs
try {
@@ -1435,12 +1542,21 @@
if (this.receiveData.tokensBase64.length == 0) {
throw new Error('no tokens provided.')
}
- const tokensJson = atob(this.receiveData.tokensBase64)
- const proofs = JSON.parse(tokensJson)
+ const tokenJson = atob(this.receiveData.tokensBase64)
+ const proofs = JSON.parse(tokenJson)
const amount = proofs.reduce((s, t) => (s += t.amount), 0)
let {fristProofs, scndProofs} = await this.split(proofs, amount)
// HACK: we need to do this so the balance updates
this.proofs = this.proofs.concat([])
+
+ this.historyTokens.push({
+ status: 'paid',
+ amount: amount,
+ date: currentDateStr(),
+ token: this.receiveData.tokensBase64
+ })
+ this.storehistoryTokens()
+
navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
@@ -1467,6 +1583,15 @@
this.sendData.tokens = scndProofs
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
+
+ this.historyTokens.push({
+ status: 'pending',
+ amount: -this.sendData.amount,
+ date: currentDateStr(),
+ token: this.sendData.tokensBase64
+ })
+ this.storehistoryTokens()
+
navigator.vibrate(200)
},
checkFees: async function (payment_request) {
@@ -1559,7 +1684,22 @@
throw error
}
},
-
+ setInvoicePaid: async function (payment_hash) {
+ const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
+ invoice.status = 'paid'
+ this.storeinvoicesCashu()
+ },
+ recheckInvoice: async function (payment_hash, verbose = true) {
+ console.log('### recheckInvoice.hash', payment_hash)
+ const invoice = this.invoicesCashu.find(i => i.hash === payment_hash)
+ try {
+ proofs = await this.mint(invoice.amount, invoice.hash, verbose)
+ return proofs
+ } catch (error) {
+ console.log('Invoice still pending')
+ throw error
+ }
+ },
recheckPendingInvoices: async function () {
for (const invoice of this.invoicesCashu) {
if (invoice.status === 'pending' && invoice.sat > 0) {
@@ -1567,7 +1707,53 @@
}
}
},
-
+ setTokenPaid: async function (token) {
+ const invoice = this.historyTokens.find(i => i.token === token)
+ invoice.status = 'paid'
+ this.storehistoryTokens()
+ },
+ checkTokenSpendable: async function(token) {
+ const tokenJson = atob(token)
+ const proofs = JSON.parse(tokenJson)
+ const payload = {
+ proofs: proofs.flat(),
+ }
+ console.log('#### payload', JSON.stringify(payload))
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ `/cashu/api/v1/${this.mintId}/check`,
+ '',
+ payload
+ )
+ // iterate through response of form {0: true, 1: false, ...}
+ let paid = false
+ for (const [key, spendable] of Object.entries(data)) {
+ if (!spendable){
+ this.setTokenPaid(token)
+ paid = true
+ }
+ }
+ if (paid){
+ navigator.vibrate(200)
+ this.$q.notify({
+ timeout: 5000,
+ type: 'positive',
+ message: 'Token sent'
+ })
+ } else {
+ this.$q.notify({
+ timeout: 5000,
+ color: 'gray',
+ message: 'Token still pending'
+ })
+ }
+ } catch (error) {
+ console.error(error)
+ LNbits.utils.notifyApiError(error)
+ throw error
+ }
+ },
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
'GET',
@@ -1577,6 +1763,7 @@
localStorage.setItem(this.mintKey(this.mintId, 'keys'), JSON.stringify(data))
},
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1650,6 +1837,12 @@
JSON.stringify(this.invoicesCashu)
)
},
+ storehistoryTokens: function () {
+ localStorage.setItem(
+ this.mintKey(this.mintId, 'historyTokens'),
+ JSON.stringify(this.historyTokens)
+ )
+ },
storeProofs: function () {
localStorage.setItem(
this.mintKey(this.mintId, 'proofs'),
@@ -1718,6 +1911,11 @@
this.invoicesCashu = JSON.parse(
localStorage.getItem(this.mintKey(this.mintId, 'invoicesCashu')) || '[]'
)
+
+ this.historyTokens = JSON.parse(
+ localStorage.getItem(this.mintKey(this.mintId, 'historyTokens')) || '[]'
+ )
+
this.proofs = JSON.parse(localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]')
console.log('### invoicesCashu', this.invoicesCashu)
console.table('### tokens', this.proofs)
From 6e59f86778d94428d80aca4e9fb3a8db322ec6b2 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 20:00:24 +0100
Subject: [PATCH 0328/1058] invoice and token check worker
---
.../cashu/templates/cashu/wallet.html | 76 ++++++++++++++++---
1 file changed, 65 insertions(+), 11 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 6d86ff35..c67f25e1 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -201,7 +201,7 @@
@@ -913,6 +913,8 @@
}
],
pagination: {
+ sortBy: 'date',
+ descending: true,
rowsPerPage: 5
},
filter: null
@@ -1230,6 +1232,8 @@
console.log('##### showInvoiceDialog')
this.invoiceData = _.clone(data)
this.showInvoiceDetails = true
+ // kick off invoice check worker
+ this.invoiceCheckWorker()
},
showPayInvoiceDialog: function () {
@@ -1240,7 +1244,18 @@
this.payInvoiceData.camera.show = false
},
+ showTokenDialog: function (token) {
+ console.log('##### showTokenDialog')
+ // TODO: this must be decoded and desiarlized!
+ this.sendData.tokens = _.clone(token)
+ this.sendData.tokensBase64 = _.clone(token)
+ this.showSendTokens = true
+ // kick off token check worker
+ this.checkTokenSpendableWorker()
+ },
+
showSendTokensDialog: function () {
+ console.log('##### showSendTokensDialog')
this.sendData.tokens = ''
this.sendData.tokensBase64 = ''
this.sendData.amount = 0
@@ -1317,9 +1332,7 @@
//////////// API ///////////
- requestMintButton: async function () {
- await this.requestMint()
- console.log("#### request mint", this.invoiceData)
+ invoiceCheckWorker: async function () {
let nInterval = 0
this.invoiceCheckListener = setInterval(async () => {
try {
@@ -1353,6 +1366,12 @@
}, 3000)
},
+ requestMintButton: async function () {
+ await this.requestMint()
+ console.log("#### request mint", this.invoiceData)
+ await this.invoiceCheckWorker()
+ },
+
requestMint: async function () {
// gets an invoice from the mint to get new tokens
try {
@@ -1578,8 +1597,6 @@
this.sendData.amount,
true
)
- this.sendData.tokens = ''
- this.sendData.tokensBase64 = ''
this.sendData.tokens = scndProofs
console.log('### this.sendData.tokens', this.sendData.tokens)
this.sendData.tokensBase64 = btoa(JSON.stringify(this.sendData.tokens))
@@ -1591,8 +1608,7 @@
token: this.sendData.tokensBase64
})
this.storehistoryTokens()
-
- navigator.vibrate(200)
+ this.checkTokenSpendableWorker()
},
checkFees: async function (payment_request) {
const payload = {
@@ -1712,7 +1728,38 @@
invoice.status = 'paid'
this.storehistoryTokens()
},
- checkTokenSpendable: async function(token) {
+
+
+ checkTokenSpendableWorker: async function () {
+ let nInterval = 0
+ this.tokensCheckSpendableListener = setInterval(async () => {
+ try {
+ nInterval += 1
+
+ // exit loop after 5m
+ if (nInterval > 100) {
+ console.log("### stopping token check worker")
+ clearInterval(this.tokensCheckSpendableListener)
+ }
+ console.log('### setInterval', nInterval)
+ console.log(this.sendData)
+
+ // this will throw an error if the invoice is pending
+ paid = await this.checkTokenSpendable(this.sendData.tokensBase64, false)
+ if (paid) {
+ console.log("### stopping token check worker")
+ clearInterval(this.tokensCheckSpendableListener)
+ this.sendData.tokens = ''
+ this.showSendTokens = false
+ }
+
+ } catch (error) {
+ console.log('not paid yet')
+ }
+ }, 3000)
+ },
+
+ checkTokenSpendable: async function(token, verbose=true) {
const tokenJson = atob(token)
const proofs = JSON.parse(tokenJson)
const payload = {
@@ -1735,25 +1782,32 @@
}
}
if (paid){
+ console.log("### token paid")
navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
- message: 'Token sent'
+ message: 'Token paid'
})
} else {
- this.$q.notify({
+ console.log("### token not paid yet")
+ if (verbose) {
+ this.$q.notify({
timeout: 5000,
color: 'gray',
message: 'Token still pending'
})
+ }
+ this.sendData.tokens = token
}
+ return paid
} catch (error) {
console.error(error)
LNbits.utils.notifyApiError(error)
throw error
}
},
+
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
'GET',
From f99dcf407c83a1c33236c110a475c403a2fd8f66 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 20:05:57 +0100
Subject: [PATCH 0329/1058] cols
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index c67f25e1..00db14f5 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -8,7 +8,7 @@
-
+
Receive invoice
-
+
{% raw %} {{getBalance()}}
@@ -28,7 +28,7 @@
-
+
Date: Sat, 5 Nov 2022 20:16:17 +0100
Subject: [PATCH 0330/1058] error handling
---
.../extensions/cashu/templates/cashu/wallet.html | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 00db14f5..f040d0fa 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1448,7 +1448,12 @@
try {
const spendableProofs = proofs.filter(p => !p.reserved)
if (this.sumProofs(spendableProofs) < amount) {
- throw new Error('balance too low.')
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: 'Balance too low'
+ })
+ throw Error('balance too low.')
}
let {fristProofs, scndProofs} = await this.split(
spendableProofs,
@@ -1591,6 +1596,7 @@
},
sendTokens: async function () {
+ try {
// keep firstProofs, send scndProofs
let {fristProofs, scndProofs} = await this.splitToSend(
this.proofs,
@@ -1609,6 +1615,11 @@
})
this.storehistoryTokens()
this.checkTokenSpendableWorker()
+ } catch (error) {
+ console.error(error)
+ throw error
+ }
+
},
checkFees: async function (payment_request) {
const payload = {
@@ -1794,7 +1805,7 @@
if (verbose) {
this.$q.notify({
timeout: 5000,
- color: 'gray',
+ color: 'grey',
message: 'Token still pending'
})
}
From 40a7952265a2c14b14a444f9dc8f6db27547939d Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 20:49:11 +0100
Subject: [PATCH 0331/1058] show bolt11
---
.../cashu/templates/cashu/wallet.html | 100 ++++++++++--------
1 file changed, 55 insertions(+), 45 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index f040d0fa..3fa7fe56 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -174,6 +174,9 @@
{{props.row.date}}
+
+ {{props.row.bolt11}}
+
{{props.row.hash}}
@@ -798,51 +801,6 @@
}
},
payments: [],
- invoicesTable: {
- columns: [
- {
- name: 'status',
- align: 'left',
- label: '',
- field: 'status',
- sortable: true
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount',
- field: 'amount',
- sortable: true
- },
- // {
- // name: 'memo',
- // align: 'left',
- // label: 'Memo',
- // field: 'memo',
- // sortable: true
- // },
- {
- name: 'date',
- align: 'left',
- label: 'Date',
- field: 'date',
- sortable: true
- },
- {
- name: 'hash',
- align: 'right',
- label: 'Hash',
- field: 'hash',
- sortable: false
- }
- ],
- pagination: {
- sortBy: 'date',
- descending: true,
- rowsPerPage: 5
- },
- filter: null
- },
tokensTable: {
columns: [
@@ -880,6 +838,58 @@
},
filter: null
},
+ invoicesTable: {
+ columns: [
+ {
+ name: 'status',
+ align: 'left',
+ label: '',
+ field: 'status',
+ sortable: true
+ },
+ {
+ name: 'amount',
+ align: 'left',
+ label: 'Amount',
+ field: 'amount',
+ sortable: true
+ },
+ // {
+ // name: 'memo',
+ // align: 'left',
+ // label: 'Memo',
+ // field: 'memo',
+ // sortable: true
+ // },
+ {
+ name: 'date',
+ align: 'left',
+ label: 'Date',
+ field: 'date',
+ sortable: true
+ },
+ {
+ name: 'bolt11',
+ align: 'left',
+ label: 'Payment request',
+ field: 'bolt11',
+ sortable: false
+ },
+ {
+ name: 'hash',
+ align: 'left',
+ label: 'Hash',
+ field: 'hash',
+ sortable: false
+ }
+ ],
+ pagination: {
+ sortBy: 'date',
+ descending: true,
+ rowsPerPage: 5
+ },
+ filter: null
+ },
historyTable: {
columns: [
From 02f8a6330f93305179c6b1cf9cc91d152a71a257 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 20:52:52 +0100
Subject: [PATCH 0332/1058] sort fix
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3fa7fe56..bbab1c1c 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -903,7 +903,7 @@
{
name: 'amount',
align: 'left',
- label: 'Value ({{LNBITS_DENOMINATION}})',
+ label: 'Amount',
field: 'amount',
sortable: true
},
@@ -982,7 +982,7 @@
return acc
}, {})
return Object.keys(x).map(k => ({
- value: k,
+ value: parseInt(k),
count: x[k],
sum: k * x[k]
}))
From 89d7f7218fc429291331180d65f47674cb0752ba Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 21:01:46 +0100
Subject: [PATCH 0333/1058] remove arrows
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index bbab1c1c..20f51c5c 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -50,7 +50,6 @@
Date: Sat, 5 Nov 2022 21:09:00 +0100
Subject: [PATCH 0334/1058] warning page
---
.../cashu/templates/cashu/wallet.html | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 20f51c5c..e247c17e 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -492,15 +492,18 @@
Warning
Bookmark this page!
-
-
Ecash is a bearer asset, meaning losing access to this wallet will mean you will
- lose the funds. This wallet stores tokens in its database. If you lose the link or request
- your data, you will lose your tokens. Bookmark this page or press the
- hamburger icon and add this page to your home screen.
+ lose the funds. This wallet stores tokens in its database. If you lose the link or delete your
+ your browser data, you will lose your tokens. Bookmark this page.
-
- This service is in BETA, and we hold no responsibility for people losing
+
+ Add to home screen.
+ You can add Cashu to your home screen as a progressive web app (PWA).
+ On Android Chrome, click the hamburger menu at the upper right.
+ On iOS Safari, click the share button.
+
+
+ This service is in BETA! We hold no responsibility for people losing
access to funds. Use at your own risk!
From bb76a9e0a9ae715adc46ae4e5d0f46ba459af3e7 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 22:08:15 +0100
Subject: [PATCH 0335/1058] input fields
---
.../cashu/templates/cashu/wallet.html | 23 +++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index e247c17e..381d6351 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -526,6 +526,7 @@
+
+
+
+
+
+
+
+
@@ -1232,7 +1247,7 @@
/////////////////////////////////// WALLET ///////////////////////////////////
showInvoicesDialog: async function () {
console.log('##### showInvoicesDialog')
- this.invoiceData.amount = 0
+ this.invoiceData.amount = ''
this.invoiceData.bolt11 = ''
this.invoiceData.hash = ''
this.invoiceData.memo = ''
@@ -1269,7 +1284,7 @@
console.log('##### showSendTokensDialog')
this.sendData.tokens = ''
this.sendData.tokensBase64 = ''
- this.sendData.amount = 0
+ this.sendData.amount = ''
this.sendData.memo = ''
this.showSendTokens = true
},
From 5bd7d3dc7ebbf303f375166d4a2c00c4f2f81575 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 22:12:23 +0100
Subject: [PATCH 0336/1058] buttons flat
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 381d6351..3c781ac3 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -445,7 +445,7 @@
color="primary"
:disable="payInvoiceData.data.request == ''"
type="submit"
- outline
+ flat
>Continue
@@ -563,7 +563,7 @@
color="primary"
>Copy invoice
-
Create Invoice
Send Tokens
@@ -673,7 +674,7 @@
-
Receive Tokens
Date: Sat, 5 Nov 2022 22:32:36 +0100
Subject: [PATCH 0337/1058] backup button
---
.../cashu/templates/cashu/wallet.html | 43 ++++++++++++++++++-
1 file changed, 41 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 3c781ac3..be4fd126 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -248,13 +248,22 @@
-
@@ -511,6 +520,7 @@
I understand
+
@@ -1923,6 +1933,36 @@
}
},
+ ////////////// STORAGE /////////////
+
+ getLocalstorageToFile: async function() {
+ // https://stackoverflow.com/questions/24263682/save-restore-local-storage-to-a-local-file
+ const fileName = `cashu_backup_${currentDateStr()}.json`
+ var a = {};
+ for (var i = 0; i < localStorage.length; i++) {
+ var k = localStorage.key(i);
+ var v = localStorage.getItem(k);
+ a[k] = v;
+ }
+ var textToSave = JSON.stringify(a)
+ var textToSaveAsBlob = new Blob([textToSave], {
+ type: "text/plain"
+ });
+ var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
+
+ var downloadLink = document.createElement("a");
+ downloadLink.download = fileName;
+ downloadLink.innerHTML = "Download File";
+ downloadLink.href = textToSaveAsURL;
+ downloadLink.onclick = function () {
+ document.body.removeChild(event.target);
+ };
+ downloadLink.style.display = "none";
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+
+ },
+
storeinvoicesCashu: function () {
localStorage.setItem(
this.mintKey(this.mintId, 'invoicesCashu'),
@@ -2013,7 +2053,6 @@
console.table('### tokens', this.proofs)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
-
this.recheckPendingInvoices()
}
})
From b005f4afeac3c36932eea7fc9acf10f540181fc7 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 22:35:07 +0100
Subject: [PATCH 0338/1058] tooltip
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index be4fd126..b6919e85 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -256,14 +256,14 @@
color="warning"
outline
@click="showDisclaimerDialog"> Warning
-
Backup
+ tooltip="asd"
+ @click="getLocalstorageToFile">BackupDownload wallet backup
From 11cc8262bc960c98e32836b30dd70357fc528d4b Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 23:12:25 +0100
Subject: [PATCH 0339/1058] block button
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index b6919e85..7eb81c9d 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -307,7 +307,7 @@
{% endraw %}
-
Pay
+
Pay
Cancel
@@ -759,6 +759,7 @@
},
invoiceCheckListener: () => {},
payInvoiceData: {
+ blocking: false,
// invoice: '',
bolt11: '',
// camera: {
@@ -1590,6 +1591,7 @@
return {fristProofs, scndProofs}
} catch (error) {
+ this.payInvoiceData.blocking = false
console.error(error)
LNbits.utils.notifyApiError(error)
throw error
@@ -1680,6 +1682,8 @@
},
melt: async function () {
// todo: get fees from server and add to inputs
+ this.payInvoiceData.blocking = true
+
console.log('#### pay lightning')
const amount_invoice = this.payInvoiceData.invoice.sat
const amount =
@@ -1718,9 +1722,8 @@
type: 'positive',
message: 'Invoice paid'
})
- // delete tokens from db
+ // delete spent tokens from db
this.proofs = fristProofs
- // add new fristProofs, scndProofs to this.proofs
this.storeProofs()
console.log({
amount: -amount,
@@ -1741,8 +1744,10 @@
this.payInvoiceData.invoice = false
this.payInvoiceData.show = false
+ this.payInvoiceData.blocking = false
} catch (error) {
+ this.payInvoiceData.blocking = false
console.error(error)
LNbits.utils.notifyApiError(error)
throw error
From 6d5014076068fefb3d4a518c68c0a77023f1aa46 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sat, 5 Nov 2022 23:50:31 +0100
Subject: [PATCH 0340/1058] vibrate check
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 7eb81c9d..b7b5334f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1392,7 +1392,7 @@
clearInterval(this.invoiceCheckListener)
this.invoiceData.bolt11 = ''
this.showInvoiceDetails = false
- navigator.vibrate(200)
+ if (window.navigator.vibrate) navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
@@ -1620,7 +1620,7 @@
})
this.storehistoryTokens()
- navigator.vibrate(200)
+ if (window.navigator.vibrate) navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
@@ -1716,7 +1716,7 @@
'',
payload
)
- navigator.vibrate(200)
+ if (window.navigator.vibrate) navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
@@ -1836,7 +1836,7 @@
}
if (paid){
console.log("### token paid")
- navigator.vibrate(200)
+ if (window.navigator.vibrate) navigator.vibrate(200)
this.$q.notify({
timeout: 5000,
type: 'positive',
From 08b4e8e5e4503ba3b565989364f72463393c4479 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 00:04:24 +0100
Subject: [PATCH 0341/1058] add seconds
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index b7b5334f..25964cf0 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -724,12 +724,12 @@
From de3060cf700f36e078ac80f071107f1a238011d7 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 12:48:33 +0100
Subject: [PATCH 0359/1058] auto check
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index cc558e93..2b2462e4 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1847,9 +1847,7 @@ page_container %}
recheckPendingTokens: async function () {
for (const token of this.historyTokens) {
- console.log(token.status, token.amount)
if (token.status === 'pending' && token.amount < 0) {
- print('CHEKABLE')
this.checkTokenSpendable(token.token, false)
}
}
From 8dd7b8c4257223bd10a29b335b9bf2ae4e487fed Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 13:03:12 +0100
Subject: [PATCH 0360/1058] check if token already received
---
.../extensions/cashu/templates/cashu/wallet.html | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 2b2462e4..7a056644 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -2149,8 +2149,18 @@ page_container %}
// get recv_token
if (params.get('recv_token')) {
- this.receiveData.tokensBase64 = params.get('recv_token')
- this.showReceiveTokens = true
+ tokenBase64 = params.get('recv_token')
+ let seen = false
+ for (var i = 0; i < this.historyTokens.length; i++) {
+ var thisToken = this.historyTokens[i].token
+ if (thisToken == tokenBase64) {
+ seen = true
+ }
+ }
+ if (!seen) {
+ this.receiveData.tokensBase64 = params.get('recv_token')
+ this.showReceiveTokens = true
+ }
}
console.log('### invoicesCashu', this.invoicesCashu)
From ef2b4b4194f8ba811eb000863001f713d55385cd Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 13:03:57 +0100
Subject: [PATCH 0361/1058] check if token already received
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 7a056644..86889bcb 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -2153,7 +2153,7 @@ page_container %}
let seen = false
for (var i = 0; i < this.historyTokens.length; i++) {
var thisToken = this.historyTokens[i].token
- if (thisToken == tokenBase64) {
+ if (thisToken == tokenBase64 && this.historyTokens[i].amount > 0) {
seen = true
}
}
From 793745a48e54891115e9e1b14650e3643e77412e Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 13:12:59 +0100
Subject: [PATCH 0362/1058] logging
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 86889bcb..23762558 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1070,14 +1070,12 @@ page_container %}
.reduce((sum, el) => (sum += el.amount), 0)
},
getTokenList: function () {
- console.log(this.proofs)
const amounts = this.proofs.map(t => t.amount)
const counts = {}
for (const num of amounts) {
counts[num] = counts[num] ? counts[num] + 1 : 1
}
- console.log('counts', counts)
return Object.keys(counts).map(k => ({
value: parseInt(k),
count: parseInt(counts[k]),
From 483994cedf1f5c3f3ee31fe750e2a9d597249900 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 13:31:02 +0100
Subject: [PATCH 0363/1058] link in QR
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 23762558..5cf3e2da 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -684,7 +684,7 @@ page_container %}
From 9912c18992101c354395c13287e7ff21d7e7c66f Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:45:17 +0100
Subject: [PATCH 0364/1058] fix scroll
---
.../cashu/templates/cashu/wallet.html | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 5cf3e2da..07700d05 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -5,7 +5,7 @@ page_container %}
-
+
@@ -67,9 +67,10 @@ page_container %}
rectangle
unelevated
color="primary"
+ icon="file_download"
class="full-width"
@click="showReceiveTokensDialog"
- >Receive TokensGet Tokens
@@ -79,10 +80,11 @@ page_container %}
rectangle
unelevated
color="primary"
+ icon="file_upload"
class="full-width"
@click="showSendTokensDialog"
>
- Send Tokens
@@ -280,7 +282,7 @@ page_container %}
-
+
BackupDownload wallet backup
@@ -308,22 +308,22 @@ page_container %}
From e5bb3a13cd84d27a82f78fe96282cd59e4eabb5d Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:46:35 +0100
Subject: [PATCH 0365/1058] faster
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 07700d05..06d9588f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1884,7 +1884,7 @@ page_container %}
} catch (error) {
console.log('not paid yet')
}
- }, 5000)
+ }, 3000)
},
checkTokenSpendable: async function (token, verbose = true) {
From 7ae6aa787f76a68a1d9a9dedbeb6c6b2bed4f1c9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:48:42 +0100
Subject: [PATCH 0366/1058] ecash
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 06d9588f..0ddb2f0a 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -70,7 +70,7 @@ page_container %}
icon="file_download"
class="full-width"
@click="showReceiveTokensDialog"
- >Get TokensGet Ecash
@@ -84,7 +84,7 @@ page_container %}
class="full-width"
@click="showSendTokensDialog"
>
- Pay Tokens
From c56da0b53049e15aa21858aaa0f823c5107ce4cc Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 18:01:54 +0100
Subject: [PATCH 0367/1058] update height
---
.../cashu/templates/cashu/wallet.html | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0ddb2f0a..9928b26a 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -5,7 +5,7 @@ page_container %}
-
+
@@ -315,7 +315,7 @@ page_container %}
@@ -2161,6 +2161,20 @@ page_container %}
}
}
+ var body = document.body,
+ html = document.documentElement
+
+ var height = Math.max(
+ body.scrollHeight,
+ body.offsetHeight,
+ html.clientHeight,
+ html.scrollHeight,
+ html.offsetHeight
+ )
+
+ console.log('height', height)
+ this.height = height
+
console.log('### invoicesCashu', this.invoicesCashu)
console.table('### tokens', this.proofs)
console.log('#### this.mintId', this.mintId)
From 16107526b8e2600f21926c5d12614c173a1551a5 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 18:37:40 +0100
Subject: [PATCH 0368/1058] label
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 9928b26a..9b49a063 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -18,7 +18,7 @@ page_container %}
rectangle
color="primary"
@click="showInvoicesDialog"
- >Receive invoice
+ >Get invoice
From bd5398fea07be997baccbb43a1241b903971d2c9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:09:08 +0100
Subject: [PATCH 0369/1058] new icons
---
lnbits/extensions/cashu/views.py | 141 ++++++++++++++++++++++++++++---
1 file changed, 127 insertions(+), 14 deletions(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 954936b9..a1469d0a 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -61,12 +61,12 @@ async def manifest(cashu_id: str):
"name": "Cashu" + " - " + cashu.name,
"icons": [
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
"type": "image/png",
"sizes": "512x512",
},
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png",
"type": "image/png",
"sizes": "96x96",
},
@@ -85,19 +85,132 @@ async def manifest(cashu_id: str):
"url": "/cashu/wallet?mint_id=" + cashu_id,
"icons": [
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
- "sizes": "96x96",
- "type": "image/png",
- },
- {
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/180x180.png",
- "sizes": "180x180",
- "type": "image/png",
- },
- {
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
"sizes": "512x512",
- "type": "image/png",
+ },
+ {
+ "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",
},
],
}
From 2b0a1ce347427247b0e6c7d441a803a9c68a1734 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:11:54 +0100
Subject: [PATCH 0370/1058] fix nanifest
---
lnbits/extensions/cashu/views.py | 129 ++-----------------------------
1 file changed, 8 insertions(+), 121 deletions(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index a1469d0a..954936b9 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -61,12 +61,12 @@ async def manifest(cashu_id: str):
"name": "Cashu" + " - " + cashu.name,
"icons": [
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
"type": "image/png",
"sizes": "512x512",
},
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"type": "image/png",
"sizes": "96x96",
},
@@ -85,132 +85,19 @@ async def manifest(cashu_id: str):
"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",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
"sizes": "96x96",
+ "type": "image/png",
},
{
- "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",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/180x180.png",
"sizes": "180x180",
+ "type": "image/png",
},
{
- "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",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
"sizes": "512x512",
- },
- {
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/1024.png",
- "sizes": "1024x1024",
+ "type": "image/png",
},
],
}
From cd5ca41012cef0fd56d0f0e2f2ce871390d97acd Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:12:18 +0100
Subject: [PATCH 0371/1058] fix nanifest
---
lnbits/extensions/cashu/views.py | 141 ++++++++++++++++++++++++++++---
1 file changed, 127 insertions(+), 14 deletions(-)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index 954936b9..a1469d0a 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -61,12 +61,12 @@ async def manifest(cashu_id: str):
"name": "Cashu" + " - " + cashu.name,
"icons": [
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
"type": "image/png",
"sizes": "512x512",
},
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png",
"type": "image/png",
"sizes": "96x96",
},
@@ -85,19 +85,132 @@ async def manifest(cashu_id: str):
"url": "/cashu/wallet?mint_id=" + cashu_id,
"icons": [
{
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/96x96.png",
- "sizes": "96x96",
- "type": "image/png",
- },
- {
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/180x180.png",
- "sizes": "180x180",
- "type": "image/png",
- },
- {
- "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/512x512.png",
+ "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png",
"sizes": "512x512",
- "type": "image/png",
+ },
+ {
+ "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",
},
],
}
From f4da8a9e2ba8929cd63962474fccb46960e227b9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:19:23 +0100
Subject: [PATCH 0372/1058] id
---
lnbits/extensions/cashu/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py
index a1469d0a..8ea628df 100644
--- a/lnbits/extensions/cashu/views.py
+++ b/lnbits/extensions/cashu/views.py
@@ -71,6 +71,7 @@ async def manifest(cashu_id: str):
"sizes": "96x96",
},
],
+ "id": "/cashu/wallet?mint_id=" + cashu_id,
"start_url": "/cashu/wallet?mint_id=" + cashu_id,
"background_color": "#1F2234",
"description": "Cashu ecash wallet",
From 4ee571ca7139ffbfb132522fb29d7d1f9f23ea8a Mon Sep 17 00:00:00 2001
From: ChuckNorrison <2964146+ChuckNorrison@users.noreply.github.com>
Date: Mon, 7 Nov 2022 11:30:18 +0100
Subject: [PATCH 0373/1058] Documentation for postgresql default port (#1106)
* Documentation for postgresql default port
A port is necessary for postgresql config. Add postgresql default port 5432 in documentation for LNBITS_DATABASE_URL config
* Update docs/guide/installation.md
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
docs/guide/installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index bf40418d..072c4d91 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -220,8 +220,8 @@ You need to edit the `.env` file.
```sh
# add the database connection string to .env 'nano .env' LNBITS_DATABASE_URL=
-# postgres://
:@/ - alter line bellow with your user, password and db name
-LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost/lnbits"
+# postgres://:@:/ - alter line bellow with your user, password and db name
+LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost:5432/lnbits"
# save and exit
```
From c3ef4d726038aa81b9298afce682d573f9aa12d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 7 Nov 2022 11:30:44 +0100
Subject: [PATCH 0374/1058] formatting fixes, prettier (#1108)
---
.../lnurldevice/templates/lnurldevice/index.html | 2 +-
.../templates/usermanager/_api_docs.html | 15 ++++++++-------
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
index 83ff4571..b0b223ff 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
@@ -797,7 +797,7 @@
LNbits.utils.notifyApiError(error)
})
},
- clearFormDialoglnurldevice () {
+ clearFormDialoglnurldevice() {
this.formDialoglnurldevice.data = {
lnurl_toggle: false,
show_message: false,
diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
index de477834..36593d74 100644
--- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
+++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
@@ -57,13 +57,14 @@
/usermanager/api/v1/users/<user_id>
Body (application/json)
-
+
Returns 200 OK (application/json)
{"id": <string>, "name": <string>, "admin":
- <string>, "email": <string>, "password": <string>}
Curl example
@@ -259,15 +260,15 @@
{"X-Api-Key": <string>}
Curl example
curl -X POST {{ request.base_url }}usermanager/api/v1/extensions?extension=withdraw&userid=user_id&active=true -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H
- "Content-type: application/json"
+ >curl -X POST {{ request.base_url
+ }}usermanager/api/v1/extensions?extension=withdraw&userid=user_id&active=true
+ -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H "Content-type:
+ application/json"
Returns 200 OK (application/json)
- {"extension": "updated"}
+ {"extension": "updated"}
From 0ea225b87e19e2e2b72657f3da0f8095b99d5257 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Mon, 7 Nov 2022 13:31:42 +0100
Subject: [PATCH 0375/1058] add api docs button
---
lnbits/templates/base.html | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html
index 67241bb5..ef270371 100644
--- a/lnbits/templates/base.html
+++ b/lnbits/templates/base.html
@@ -199,6 +199,18 @@
>
+
+ API DOCS
+ View LNbits Swagger API docs
+
Date: Mon, 7 Nov 2022 14:00:05 +0100
Subject: [PATCH 0376/1058] set private key
---
.env.example | 4 ++++
lnbits/extensions/cashu/__init__.py | 9 ++++++---
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/.env.example b/.env.example
index 987c6ca6..ccf18ed5 100644
--- a/.env.example
+++ b/.env.example
@@ -97,3 +97,7 @@ ECLAIR_PASS=eclairpw
# Enter /api in LightningTipBot to get your key
LNTIPS_API_KEY=LNTIPS_ADMIN_KEY
LNTIPS_API_ENDPOINT=https://ln.tips
+
+# Cashu Mint
+# Use a long-enough random (!) private key
+CASHU_PRIVATE_KEY="SuperSecretPrivateKey"
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index 7944e658..5d5fe131 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -1,5 +1,6 @@
import asyncio
+from environs import Env # type: ignore
from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles
@@ -20,11 +21,13 @@ cashu_static_files = [
]
from cashu.mint.ledger import Ledger
+env = Env()
+env.read_env()
+
ledger = Ledger(
db=db,
- # seed=MINT_PRIVATE_KEY,
- seed="asd",
- derivation_path="0/0/0/1",
+ seed=env.str("CASHU_PRIVATE_KEY", default="SuperSecretPrivateKey"),
+ derivation_path="0/0/0/0",
)
cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
From 00e8255e0b325c414f08a87553e017e9c452f83c Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 15:36:52 +0100
Subject: [PATCH 0377/1058] scrolling works
---
.env.example | 3 +-
.../cashu/templates/cashu/wallet.html | 560 +++++++++---------
2 files changed, 283 insertions(+), 280 deletions(-)
diff --git a/.env.example b/.env.example
index ccf18ed5..7b787117 100644
--- a/.env.example
+++ b/.env.example
@@ -99,5 +99,6 @@ LNTIPS_API_KEY=LNTIPS_ADMIN_KEY
LNTIPS_API_ENDPOINT=https://ln.tips
# Cashu Mint
-# Use a long-enough random (!) private key
+# Use a long-enough random (!) private key.
+# Once set, you cannot change this key as for now.
CASHU_PRIVATE_KEY="SuperSecretPrivateKey"
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 9b49a063..a0861ae7 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -5,305 +5,307 @@ page_container %}
-
-
-
-
-
-
- Get invoice
-
-
-
-
-
- {% raw %} {{getBalance()}}
- {{tickershort}}{% endraw %}
-
-
-
-
- Pay invoice
-
-
-
-
-
-
-
-
-
- {% raw %} {{getBalance()}}
- {{tickershort}}{% endraw %}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
Get Ecash
+ size="14px"
+ icon="bolt"
+ rectangle
+ color="primary"
+ @click="showInvoicesDialog"
+ >Get invoice
+
-
-
+
+
+
+ {% raw %} {{getBalance()}}
+ {{tickershort}}{% endraw %}
+
+
+
+
- Pay Ecash
+ @click="showParseDialog"
+ size="14px"
+ icon="bolt"
+ rectangle
+ color="primary"
+ class="full-width"
+ >Pay invoice
+
+
+
+
+
+
+
+ {% raw %} {{getBalance()}}
+ {{tickershort}}{% endraw %}
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
- {% raw %}
-
-
-
- {{props.row.value}}
-
-
- {{props.row.count}}
-
-
- {{props.row.sum}}
-
-
- {{props.row.memo}}
-
-
-
- {% endraw %}
-
-
+
+
+ {% raw %}
+
+
+
+ {{props.row.value}}
+
+
+ {{props.row.count}}
+
+
+ {{props.row.sum}}
+
+
+ {{props.row.memo}}
+
+
+
+ {% endraw %}
+
+
-
+
-
-
- {% raw %}
-
-
-
-
-
- Pending
-
-
- Check
-
-
-
- Received
- Paid
-
-
-
-
- {{props.row.amount}}
-
+
+
+ {% raw %}
+
+
+
+
+
+ Pending
+
+
+ Check
+
+
+
+ Received
+ Paid
+
+
+
+
+ {{props.row.amount}}
+
-
-
- {{props.row.date}}
-
-
- {{props.row.bolt11}}
-
-
- {{props.row.hash}}
-
-
-
- {% endraw %}
-
-
+
+ {{props.row.date}}
+
+
+ {{props.row.bolt11}}
+
+
+ {{props.row.hash}}
+
+
+
+ {% endraw %}
+
+
-
+
-
-
- {% raw %}
-
-
-
-
-
- Pending
-
-
- Check
-
-
-
- Received
- Paid
-
-
-
-
- {{props.row.amount}}
-
+
+
+ {% raw %}
+
+
+
+
+
+ Pending
+
+
+ Check
+
+
+
+ Received
+ Paid
+
+
+
+
+ {{props.row.amount}}
+
-
- {{props.row.date}}
-
-
- {{props.row.token}}
-
-
-
- {% endraw %}
-
-
-
-
-
+
+ {{props.row.date}}
+
+
+ {{props.row.token}}
+
+
+
+ {% endraw %}
+
+
+
+
+
-
-
- Warning
- BackupDownload wallet backup
-
-
+
+
+ Warning
+ BackupDownload wallet backup
+
Date: Mon, 7 Nov 2022 16:16:15 +0100
Subject: [PATCH 0378/1058] camera for tokens
---
.../cashu/templates/cashu/wallet.html | 178 +++++++++++-------
1 file changed, 105 insertions(+), 73 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index a0861ae7..65e3690b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -321,7 +321,8 @@ page_container %}
@click="showInvoicesDialog"
>
-
+
+
@@ -512,11 +513,12 @@ page_container %}
unelevated
icon="photo_camera"
class="q-mx-0"
+ v-if="hasCamera"
@click="showCamera"
>
Cancel Close
@@ -537,7 +539,7 @@ page_container %}
-
+
Receive Tokens
+
Close
@@ -825,6 +834,11 @@ page_container %}
bolt11: '',
hash: ''
},
+ camera: {
+ data: null,
+ show: false,
+ camera: 'auto'
+ },
invoiceCheckListener: () => {},
payInvoiceData: {
blocking: false,
@@ -1089,10 +1103,15 @@ page_container %}
return row.payment_hash + row.amount
},
closeCamera: function () {
- this.payInvoiceData.camera.show = false
+ this.camera.show = false
},
showCamera: function () {
- this.payInvoiceData.camera.show = true
+ this.camera.show = true
+ },
+ hasCamera: function () {
+ navigator.permissions.query({name: 'camera'}).then(res => {
+ return res.state == 'granted'
+ })
},
showChart: function () {
this.paymentsChart.show = true
@@ -1124,7 +1143,7 @@ page_container %}
this.payInvoiceData.data.request = ''
this.payInvoiceData.data.comment = ''
this.payInvoiceData.data.paymentChecker = null
- this.payInvoiceData.camera.show = false
+ this.camera.show = false
this.focusInput('pasteInput')
},
showDisclaimerDialog: function () {
@@ -1203,83 +1222,96 @@ page_container %}
})
},
decodeQR: function (res) {
- this.payInvoiceData.data.request = res
+ this.camera.data = res
+ // this.payInvoiceData.data.request = res
this.decodeRequest()
- this.payInvoiceData.camera.show = false
+ this.camera.show = false
},
decodeRequest: function () {
- this.payInvoiceData.show = true
- let req = this.payInvoiceData.data.request.toLowerCase()
- if (
- this.payInvoiceData.data.request
- .toLowerCase()
- .startsWith('lightning:')
- ) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
- 10
- )
- } else if (
- this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl:')
- ) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request.slice(
- 6
- )
+ // let req = this.payInvoiceData.data.request.toLowerCase()
+ reqtype = null
+ let req = this.camera.data
+ if (req.toLowerCase().startsWith('lnbc')) {
+ this.payInvoiceData.data.request = req
+ reqtype = 'bolt11'
+ } else if (req.toLowerCase().startsWith('lightning:')) {
+ this.payInvoiceData.data.request = req.slice(10)
+ reqtype = 'bolt11'
+ } else if (req.toLowerCase().startsWith('lnurl:')) {
+ this.payInvoiceData.data.request = req.slice(6)
+ reqtype = 'lnurl'
} else if (req.indexOf('lightning=lnurl1') !== -1) {
- this.payInvoiceData.data.request = this.payInvoiceData.data.request
+ this.payInvoiceData.data.request = req
.split('lightning=')[1]
.split('&')[0]
- }
-
- if (
- this.payInvoiceData.data.request.toLowerCase().startsWith('lnurl1') ||
- this.payInvoiceData.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
+ reqtype = 'lnurl'
+ } else if (
+ req.toLowerCase().startsWith('lnurl1') ||
+ req.match(/[\w.+-~_]+@[\w.+-~_]/)
) {
+ this.payInvoiceData.data.request = req
+ reqtype = 'lnurl'
return
+ } else if (req.indexOf('W3siaWQ') !== 1) {
+ // very dirty way of parsing cashu tokens
+
+ this.receiveData.tokensBase64 = req.slice(req.indexOf('W3siaWQ'))
+ reqtype = 'cashu'
}
- let invoice
- try {
- invoice = decode(this.payInvoiceData.data.request)
- } catch (error) {
- this.$q.notify({
- timeout: 3000,
- type: 'warning',
- message: error + '.',
- caption: '400 BAD REQUEST'
- })
- this.payInvoiceData.show = false
- throw error
- return
- }
-
- let cleanInvoice = {
- msat: invoice.human_readable_part.amount,
- sat: invoice.human_readable_part.amount / 1000,
- fsat: LNbits.utils.formatSat(
- invoice.human_readable_part.amount / 1000
- )
- }
-
- _.each(invoice.data.tags, tag => {
- if (_.isObject(tag) && _.has(tag, 'description')) {
- if (tag.description === 'payment_hash') {
- cleanInvoice.hash = tag.value
- } else if (tag.description === 'description') {
- cleanInvoice.description = tag.value
- } else if (tag.description === 'expiry') {
- var expireDate = new Date(
- (invoice.data.time_stamp + tag.value) * 1000
- )
- cleanInvoice.expireDate = Quasar.utils.date.formatDate(
- expireDate,
- 'YYYY-MM-DDTHH:mm:ss.SSSZ'
- )
- cleanInvoice.expired = false // TODO
- }
+ if (reqtype == 'bolt11') {
+ console.log('#### QR CODE: BOLT11')
+ this.payInvoiceData.show = true
+ let invoice
+ try {
+ invoice = decode(this.payInvoiceData.data.request)
+ } catch (error) {
+ this.$q.notify({
+ timeout: 3000,
+ type: 'warning',
+ message: error + '.',
+ caption: '400 BAD REQUEST'
+ })
+ this.payInvoiceData.show = false
+ throw error
+ return
}
- })
- this.payInvoiceData.invoice = Object.freeze(cleanInvoice)
+ let cleanInvoice = {
+ msat: invoice.human_readable_part.amount,
+ sat: invoice.human_readable_part.amount / 1000,
+ fsat: LNbits.utils.formatSat(
+ invoice.human_readable_part.amount / 1000
+ )
+ }
+
+ _.each(invoice.data.tags, tag => {
+ if (_.isObject(tag) && _.has(tag, 'description')) {
+ if (tag.description === 'payment_hash') {
+ cleanInvoice.hash = tag.value
+ } else if (tag.description === 'description') {
+ cleanInvoice.description = tag.value
+ } else if (tag.description === 'expiry') {
+ var expireDate = new Date(
+ (invoice.data.time_stamp + tag.value) * 1000
+ )
+ cleanInvoice.expireDate = Quasar.utils.date.formatDate(
+ expireDate,
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ'
+ )
+ cleanInvoice.expired = false // TODO
+ }
+ }
+ })
+
+ this.payInvoiceData.invoice = Object.freeze(cleanInvoice)
+ } else if (reqtype == 'lnurl') {
+ console.log('#### QR CODE: LNURL')
+ // not supported yet
+ } else if (reqtype == 'cashu') {
+ console.log('#### QR CODE: CASHU TOKEN')
+ this.showReceiveTokens = true
+ }
},
payInvoice: function () {
let dismissPaymentMsg = this.$q.notify({
@@ -1348,7 +1380,7 @@ page_container %}
this.payInvoiceData.invoice = ''
this.payInvoiceData.data.request = ''
this.showPayInvoice = true
- this.payInvoiceData.camera.show = false
+ this.camera.show = false
},
showTokenDialog: function (token) {
From 2db8006d9e904b21e68b23908f6ea516ebe4a1bf Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 16:26:39 +0100
Subject: [PATCH 0379/1058] take private key from config
---
lnbits/extensions/cashu/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
index 5d5fe131..e6507bba 100644
--- a/lnbits/extensions/cashu/__init__.py
+++ b/lnbits/extensions/cashu/__init__.py
@@ -27,7 +27,7 @@ env.read_env()
ledger = Ledger(
db=db,
seed=env.str("CASHU_PRIVATE_KEY", default="SuperSecretPrivateKey"),
- derivation_path="0/0/0/0",
+ derivation_path="0/0/0/1",
)
cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
From fbd8f6a8e5e091d3392e74a94cb1648318a1f1b2 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 16:54:25 +0100
Subject: [PATCH 0380/1058] clean
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 65e3690b..e095a419 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1228,8 +1228,7 @@ page_container %}
this.camera.show = false
},
decodeRequest: function () {
- // let req = this.payInvoiceData.data.request.toLowerCase()
- reqtype = null
+ let reqtype = null
let req = this.camera.data
if (req.toLowerCase().startsWith('lnbc')) {
this.payInvoiceData.data.request = req
@@ -1252,9 +1251,11 @@ page_container %}
this.payInvoiceData.data.request = req
reqtype = 'lnurl'
return
+ } else if (req.indexOf('cashu:') !== 1) {
+ this.receiveData.tokensBase64 = req.slice(req.indexOf('cashu:'))
+ reqtype = 'cashu'
} else if (req.indexOf('W3siaWQ') !== 1) {
// very dirty way of parsing cashu tokens
-
this.receiveData.tokensBase64 = req.slice(req.indexOf('W3siaWQ'))
reqtype = 'cashu'
}
From fdb3243d543626aa4365a1da914f3872188e3e46 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:08:30 +0100
Subject: [PATCH 0381/1058] fix html
---
.../cashu/templates/cashu/wallet.html | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index e095a419..32b03478 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -758,12 +758,12 @@ page_container %}
Receive Tokens
+ unelevated
+ icon="photo_camera"
+ class="q-mx-0"
+ v-if="hasCamera"
+ @click="showCamera"
+ >
Close
@@ -2110,6 +2110,8 @@ page_container %}
}
},
+ mounted: function () {},
+
created: function () {
let params = new URL(document.location).searchParams
@@ -2180,7 +2182,7 @@ page_container %}
localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]'
)
- // get recv_token
+ // get recv_token to receive tokens from a link
if (params.get('recv_token')) {
tokenBase64 = params.get('recv_token')
let seen = false
@@ -2214,6 +2216,7 @@ page_container %}
console.table('### tokens', this.proofs)
console.log('#### this.mintId', this.mintId)
console.log('#### this.mintName', this.mintName)
+
this.recheckPendingInvoices()
this.recheckPendingTokens()
}
From 8cda2f34652dcc32c068b70d3927e61ffee342af Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:23:04 +0100
Subject: [PATCH 0382/1058] denser
---
.../extensions/cashu/templates/cashu/wallet.html | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 32b03478..0d0fd4f1 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -3,10 +3,10 @@
page_container %}
-
+
-
-
+
+
@@ -57,7 +57,7 @@ page_container %}
-
+
@@ -281,11 +281,7 @@ page_container %}
-
+
Date: Mon, 7 Nov 2022 17:31:30 +0100
Subject: [PATCH 0383/1058] invoices first
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 0d0fd4f1..79ba5c44 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -92,8 +92,8 @@ page_container %}
////////////////// TABLES /////////////////
/////////////////////////////////////////// -->
-
+
@@ -874,7 +874,7 @@ page_container %}
showReceiveTokens: false,
promises: [],
tokens: [],
- tab: 'tokens',
+ tab: 'invoices',
receive: {
show: false,
From 6ef47bd0c789618144d629f664916cf873e1c033 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:44:36 +0100
Subject: [PATCH 0384/1058] wider
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 79ba5c44..08cae6bd 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -3,9 +3,9 @@
page_container %}
-
+
-
+
From 39822fc063a3aeda519bdc9a465afb6cba41aaf8 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:45:16 +0100
Subject: [PATCH 0385/1058] wider
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 08cae6bd..9fda994b 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -5,7 +5,7 @@ page_container %}
-
+
From 6b46a5b37101782dd574e37cabb526ff1a8a4c5e Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:50:12 +0100
Subject: [PATCH 0386/1058] read qr tokens
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 9fda994b..aff8f6a4 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1247,9 +1247,9 @@ page_container %}
this.payInvoiceData.data.request = req
reqtype = 'lnurl'
return
- } else if (req.indexOf('cashu:') !== 1) {
- this.receiveData.tokensBase64 = req.slice(req.indexOf('cashu:'))
- reqtype = 'cashu'
+ // } else if (req.indexOf('cashu:') !== 1) {
+ // this.receiveData.tokensBase64 = req.slice(req.indexOf('cashu:'))
+ // reqtype = 'cashu'
} else if (req.indexOf('W3siaWQ') !== 1) {
// very dirty way of parsing cashu tokens
this.receiveData.tokensBase64 = req.slice(req.indexOf('W3siaWQ'))
From 9ad18ba292ff52b9846a376e9723699ae45959e1 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 17:53:52 +0100
Subject: [PATCH 0387/1058] string fix
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index aff8f6a4..c8e298e6 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -106,7 +106,7 @@ page_container %}
:data="getTokenList()"
:columns="tokensTable.columns"
:pagination.sync="tokensTable.pagination"
- no-data-label="No tokens yet"
+ no-data-label="There are no tokens here yet"
:filter="tokensTable.filter"
>
{% raw %}
From d955e434b0edf82decf4fcfd9a73801df835b0a7 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 18:29:54 +0100
Subject: [PATCH 0388/1058] fix invoice input
---
lnbits/extensions/cashu/templates/cashu/wallet.html | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index c8e298e6..5d704c83 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1225,7 +1225,16 @@ page_container %}
},
decodeRequest: function () {
let reqtype = null
- let req = this.camera.data
+ let req = null
+ // get request
+ if (this.camera.data) {
+ // get request from camera
+ req = this.camera.data
+ } else if (this.payInvoiceData.data.request) {
+ // get request from pay invoice dialog
+ req = this.payInvoiceData.data.request
+ }
+
if (req.toLowerCase().startsWith('lnbc')) {
this.payInvoiceData.data.request = req
reqtype = 'bolt11'
From 55f8c0b1a776dd42d67b3aabbfb9a7b753acac0a Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Mon, 7 Nov 2022 21:59:58 +0100
Subject: [PATCH 0389/1058] notification
---
.../cashu/templates/cashu/wallet.html | 201 ++++++++++++++----
lnbits/extensions/cashu/views.py | 4 +
2 files changed, 163 insertions(+), 42 deletions(-)
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index 5d704c83..2eceb5f5 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -16,7 +16,7 @@ page_container %}
icon="bolt"
rectangle
color="primary"
- @click="showInvoicesDialog"
+ @click="showInvoiceCreateDialog"
>Get invoice
@@ -152,7 +152,7 @@ page_container %}
@@ -314,7 +314,7 @@ page_container %}
class="q-pa-none"
icon="bolt"
label="Get invoice"
- @click="showInvoicesDialog"
+ @click="showInvoiceCreateDialog"
>
@@ -494,7 +494,7 @@ page_container %}
dense
v-model.trim="payInvoiceData.data.request"
type="textarea"
- label="Enter an invoice *"
+ label="Enter a Lightning invoice *"
>