Merge remote-tracking branch 'origin/main' into extension_install_02

This commit is contained in:
ben 2023-01-26 12:39:11 +00:00
commit 39f0000fa5
23 changed files with 371 additions and 170 deletions

View File

@ -4,7 +4,6 @@ docker
docs docs
tests tests
venv venv
tools
lnbits/static/css/* lnbits/static/css/*
lnbits/static/bundle.js lnbits/static/bundle.js

View File

@ -66,6 +66,9 @@ LNBITS_BACKEND_WALLET_CLASS=VoidWallet
# VoidWallet is just a fallback that works without any actual Lightning capabilities, # VoidWallet is just a fallback that works without any actual Lightning capabilities,
# just so you can see the UI before dealing with this file. # just so you can see the UI before dealing with this file.
# Invoice expiry for LND, CLN, Eclair, LNbits funding sources
LIGHTNING_INVOICE_EXPIRY=600
# Set one of these blocks depending on the wallet kind you chose above: # Set one of these blocks depending on the wallet kind you chose above:
# ClicheWallet # ClicheWallet

View File

@ -134,6 +134,49 @@ jobs:
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3
with: with:
file: ./coverage.xml file: ./coverage.xml
LNbitsWallet:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
poetry-version: ["1.3.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Set up Poetry ${{ matrix.poetry-version }}
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Setup Regtest
run: |
docker build -t lnbitsdocker/lnbits-legend .
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
cd docker
chmod +x ./tests
./tests
sudo chmod -R a+rwx .
docker exec lnbits-legend-lnbits-1 /bin/bash -c "poetry run python tools/create_fake_admin.py"
- name: Install dependencies
run: |
poetry install
- name: Run tests
env:
PYTHONUNBUFFERED: 1
PORT: 5123
LNBITS_DATA_FOLDER: ./data
LNBITS_BACKEND_WALLET_CLASS: LNbitsWallet
LNBITS_ENDPOINT: http://localhost:5001
LNBITS_KEY: "d08a3313322a4514af75d488bcc27eee"
run: |
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
make test-real-wallet
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
EclairWallet: EclairWallet:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@ -176,4 +219,4 @@ jobs:
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3
with: with:
file: ./coverage.xml file: ./coverage.xml

View File

@ -13,7 +13,7 @@ RUN mkdir -p lnbits/data
COPY . . COPY . .
RUN poetry config virtualenvs.create false RUN poetry config virtualenvs.create false
RUN poetry install --only main --no-root RUN poetry install --only main
RUN poetry run python build.py RUN poetry run python build.py
ENV LNBITS_PORT="5000" ENV LNBITS_PORT="5000"

View File

@ -26,8 +26,8 @@ sudo apt install python3.9 python3.9-distutils
curl -sSL https://install.python-poetry.org | python3 - curl -sSL https://install.python-poetry.org | python3 -
# Once the above poetry install is completed, use the installation path printed to terminal and replace in the following command # 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" export PATH="/home/user/.local/bin:$PATH"
# Next command, you can exchange with python3.10 or newer versions. # Next command, you can exchange with python3.10 or newer versions.
# Identify your version with python3 --version and specify in the next line # 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 # command is only needed when your default python is not ^3.9 or ^3.10
poetry env use python3.9 poetry env use python3.9
@ -36,7 +36,7 @@ poetry install --only main
mkdir data mkdir data
cp .env.example .env cp .env.example .env
# set funding source amongst other options # set funding source amongst other options
nano .env nano .env
``` ```
#### Running the server #### Running the server
@ -45,7 +45,7 @@ nano .env
poetry run lnbits poetry run lnbits
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0' # To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
# adding --debug in the start-up command above to help your troubleshooting and generate a 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. # Note that you have to add the line DEBUG=true in your .env file, too.
``` ```
#### Updating the server #### Updating the server
@ -58,7 +58,7 @@ poetry install --only main
# Start LNbits with `poetry run lnbits` # Start LNbits with `poetry run lnbits`
``` ```
## Option 2: Nix ## Option 2: Nix
> note: currently not supported while we make some architectural changes on the path to leave beta > note: currently not supported while we make some architectural changes on the path to leave beta
@ -105,22 +105,30 @@ If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`
## Option 4: Docker ## Option 4: Docker
use latest version from docker hub
```sh
docker pull lnbitsdocker/lnbits-legend
wget https://raw.githubusercontent.com/lnbits/lnbits/main/.env.example -O .env
mkdir data
docker run --detach --publish 5000:5000 --name lnbits --volume ${PWD}/.env:/app/.env --volume ${PWD}/data/:/app/data lnbitsdocker/lnbits-legend
```
build the image yourself
```sh ```sh
git clone https://github.com/lnbits/lnbits.git git clone https://github.com/lnbits/lnbits.git
cd lnbits cd lnbits
docker build -t lnbits . docker build -t lnbitsdocker/lnbits-legend .
cp .env.example .env cp .env.example .env
mkdir data mkdir data
docker run --detach --publish 5000:5000 --name lnbits-legend --volume ${PWD}/.env:/app/.env --volume ${PWD}/data/:/app/data lnbits-legend docker run --detach --publish 5000:5000 --name lnbits --volume ${PWD}/.env:/app/.env --volume ${PWD}/data/:/app/data lnbitsdocker/lnbits-legend
``` ```
## Option 5: Fly.io ## Option 5: Fly.io
Fly.io is a docker container hosting platform that has a generous free tier. You can host LNbits for free on Fly.io for personal use. Fly.io is a docker container hosting platform that has a generous free tier. You can host LNbits for free on Fly.io for personal use.
First, sign up for an account at [Fly.io](https://fly.io) (no credit card required). First, sign up for an account at [Fly.io](https://fly.io) (no credit card required).
Then, install the Fly.io CLI onto your device [here](https://fly.io/docs/getting-started/installing-flyctl/). Then, install the Fly.io CLI onto your device [here](https://fly.io/docs/getting-started/installing-flyctl/).
After install is complete, the command will output a command you should copy/paste/run to get `fly` into your `$PATH`. Something like: After install is complete, the command will output a command you should copy/paste/run to get `fly` into your `$PATH`. Something like:
@ -145,7 +153,7 @@ fly launch
You'll be prompted to enter an app name, region, postgres (choose no), deploy now (choose no). You'll be prompted to enter an app name, region, postgres (choose no), deploy now (choose no).
You'll now find a file in the directory called `fly.toml`. Open that file and modify/add the following settings. You'll now find a file in the directory called `fly.toml`. Open that file and modify/add the following settings.
Note: Be sure to replace `${PUT_YOUR_LNBITS_ENV_VARS_HERE}` with all relevant environment variables in `.env` or `.env.example`. Environment variable strings should be quoted here, so if in `.env` you have `LNBITS_ENDPOINT=https://legend.lnbits.com` in `fly.toml` you should have `LNBITS_ENDPOINT="https://legend.lnbits.com"`. Note: Be sure to replace `${PUT_YOUR_LNBITS_ENV_VARS_HERE}` with all relevant environment variables in `.env` or `.env.example`. Environment variable strings should be quoted here, so if in `.env` you have `LNBITS_ENDPOINT=https://legend.lnbits.com` in `fly.toml` you should have `LNBITS_ENDPOINT="https://legend.lnbits.com"`.
@ -169,7 +177,7 @@ kill_timeout = 30
LNBITS_FORCE_HTTPS=true LNBITS_FORCE_HTTPS=true
FORWARDED_ALLOW_IPS="*" FORWARDED_ALLOW_IPS="*"
LNBITS_DATA_FOLDER="/data" LNBITS_DATA_FOLDER="/data"
${PUT_YOUR_LNBITS_ENV_VARS_HERE} ${PUT_YOUR_LNBITS_ENV_VARS_HERE}
... ...

View File

@ -65,6 +65,7 @@ async def create_invoice(
memo: str, memo: str,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
expiry: Optional[int] = None,
extra: Optional[Dict] = None, extra: Optional[Dict] = None,
webhook: Optional[str] = None, webhook: Optional[str] = None,
internal: Optional[bool] = False, internal: Optional[bool] = False,
@ -80,6 +81,7 @@ async def create_invoice(
memo=invoice_memo, memo=invoice_memo,
description_hash=description_hash, description_hash=description_hash,
unhashed_description=unhashed_description, unhashed_description=unhashed_description,
expiry=expiry or settings.lightning_invoice_expiry,
) )
if not ok: if not ok:
raise InvoiceFailure(error_message or "unexpected backend error.") raise InvoiceFailure(error_message or "unexpected backend error.")

View File

@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<div class="col-12 col-md-6"> <div class="col-12 col-md-4">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<p>Active Funding<small> (Requires server restart)</small></p> <p>Active Funding<small> (Requires server restart)</small></p>
@ -30,28 +30,40 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-8">
<div class="col-12"> <div class="row q-col-gutter-md">
<p>Fee reserve</p> <div class="col-12 col-md-4">
<div class="row q-col-gutter-md"> <p>Invoice Expiry</p>
<div class="col-6"> <q-input
<q-input filled
type="number" v-model="formData.lightning_invoice_expiry"
filled label="Invoice expiry (seconds)"
v-model="formData.lnbits_reserve_fee_min" mask="#######"
label="Reserve fee in msats" >
> </q-input>
</q-input> </div>
</div> <div class="col-12 col-md-8">
<div class="col-6"> <p>Fee reserve</p>
<q-input <div class="row q-col-gutter-md">
type="number" <div class="col-6">
filled <q-input
name="lnbits_reserve_fee_percent" type="number"
v-model="formData.lnbits_reserve_fee_percent" filled
label="Reserve fee in percent" v-model="formData.lnbits_reserve_fee_min"
step="0.1" label="Reserve fee in msats"
></q-input> >
</q-input>
</div>
<div class="col-6">
<q-input
type="number"
filled
name="lnbits_reserve_fee_percent"
v-model="formData.lnbits_reserve_fee_percent"
label="Reserve fee in percent"
step="0.1"
></q-input>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -9,6 +9,7 @@
:disabled="!checkChanges" :disabled="!checkChanges"
> >
<q-tooltip v-if="checkChanges"> Save your changes </q-tooltip> <q-tooltip v-if="checkChanges"> Save your changes </q-tooltip>
<q-badge <q-badge
v-if="checkChanges" v-if="checkChanges"
color="red" color="red"
@ -17,6 +18,7 @@
style="padding: 6px; border-radius: 6px" style="padding: 6px; border-radius: 6px"
/> />
</q-btn> </q-btn>
<q-btn <q-btn
v-if="isSuperUser" v-if="isSuperUser"
label="Restart server" label="Restart server"
@ -26,6 +28,7 @@
<q-tooltip v-if="needsRestart"> <q-tooltip v-if="needsRestart">
Restart the server for changes to take effect Restart the server for changes to take effect
</q-tooltip> </q-tooltip>
<q-badge <q-badge
v-if="needsRestart" v-if="needsRestart"
color="red" color="red"
@ -34,6 +37,7 @@
style="padding: 6px; border-radius: 6px" style="padding: 6px; border-radius: 6px"
/> />
</q-btn> </q-btn>
<q-btn <q-btn
v-if="isSuperUser" v-if="isSuperUser"
label="Topup" label="Topup"
@ -42,11 +46,13 @@
> >
<q-tooltip> Add funds to a wallet. </q-tooltip> <q-tooltip> Add funds to a wallet. </q-tooltip>
</q-btn> </q-btn>
<!-- <q-btn <!-- <q-btn
label="Download Database Backup" label="Download Database Backup"
flat flat
@click="downloadBackup" @click="downloadBackup"
></q-btn> --> ></q-btn> -->
<q-btn <q-btn
flat flat
v-if="isSuperUser" v-if="isSuperUser"
@ -59,6 +65,7 @@
</q-btn> </q-btn>
</div> </div>
</div> </div>
<div class="row q-col-gutter-md justify-center"> <div class="row q-col-gutter-md justify-center">
<div class="col q-gutter-y-md"> <div class="col q-gutter-y-md">
<q-card> <q-card>
@ -70,16 +77,19 @@
label="Funding" label="Funding"
@update="val => tab = val.name" @update="val => tab = val.name"
></q-tab> ></q-tab>
<q-tab <q-tab
name="users" name="users"
label="Users" label="Users"
@update="val => tab = val.name" @update="val => tab = val.name"
></q-tab> ></q-tab>
<q-tab <q-tab
name="server" name="server"
label="Server" label="Server"
@update="val => tab = val.name" @update="val => tab = val.name"
></q-tab> ></q-tab>
<q-tab <q-tab
name="theme" name="theme"
label="Theme" label="Theme"
@ -88,6 +98,7 @@
</q-tabs> </q-tabs>
</div> </div>
</div> </div>
<q-form name="settings_form" id="settings_form"> <q-form name="settings_form" id="settings_form">
<q-tab-panels v-model="tab" animated> <q-tab-panels v-model="tab" animated>
{% include "admin/_tab_funding.html" %} {% include {% include "admin/_tab_funding.html" %} {% include
@ -98,10 +109,12 @@
</q-card> </q-card>
</div> </div>
</div> </div>
<q-dialog v-if="isSuperUser" v-model="topUpDialog.show" position="top"> <q-dialog v-if="isSuperUser" v-model="topUpDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card"> <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form class="q-gutter-md"> <q-form class="q-gutter-md">
<p>TopUp a wallet</p> <p>TopUp a wallet</p>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<q-input <q-input
@ -112,8 +125,10 @@
label="Wallet ID" label="Wallet ID"
hint="Use the wallet ID to topup any wallet" hint="Use the wallet ID to topup any wallet"
></q-input> ></q-input>
<br /> <br />
</div> </div>
<div class="col-12"> <div class="col-12">
<q-input <q-input
dense dense
@ -124,14 +139,15 @@
></q-input> ></q-input>
</div> </div>
</div> </div>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn label="Topup" color="primary" @click="topupWallet"></q-btn> <q-btn label="Topup" color="primary" @click="topupWallet"></q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn> <q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div> </div>
</q-form> </q-form>
</q-card> </q-card>
</q-dialog> </q-dialog>
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} {% block scripts %} {{ window_vars(user) }}
<script> <script>
new Vue({ new Vue({
@ -174,7 +190,7 @@
} }
], ],
[ [
'CLightningWallet', 'CoreLightningWallet',
{ {
corelightning_rpc: { corelightning_rpc: {
value: null, value: null,
@ -245,15 +261,15 @@
} }
], ],
[ [
'LntxbotWallet', 'LnTipsWallet',
{ {
lntxbot_api_endpoint: { lntips_api_endpoint: {
value: null, value: null,
label: 'Endpoint' label: 'Endpoint'
}, },
lntxbot_key: { lntips_api_key: {
value: null, value: null,
label: 'Key' label: 'API Key'
} }
} }
], ],
@ -279,7 +295,7 @@
{ {
eclair_url: { eclair_url: {
value: null, value: null,
label: 'Endpoint' label: 'URL'
}, },
eclair_pass: { eclair_pass: {
value: null, value: null,
@ -334,19 +350,6 @@
label: 'Token' label: 'Token'
} }
} }
],
[
'LnTipsWallet',
{
lntips_api_endpoint: {
value: null,
label: 'Endpoint'
},
lntips_api_key: {
value: null,
label: 'API Key'
}
}
] ]
]) ])
} }

View File

@ -48,9 +48,9 @@
<code>{"X-Api-Key": "<i>{{ wallet.inkey }}</i>"}</code><br /> <code>{"X-Api-Key": "<i>{{ wallet.inkey }}</i>"}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5> <h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
<code <code
>{"out": false, "amount": &lt;int&gt;, "memo": &lt;string&gt;, "unit": >{"out": false, "amount": &lt;int&gt;, "memo": &lt;string&gt;,
&lt;string&gt;, "webhook": &lt;url:string&gt;, "internal": "expiry": &lt;int&gt;, "unit": &lt;string&gt;, "webhook":
&lt;bool&gt;}</code &lt;url:string&gt;, "internal": &lt;bool&gt;}</code
> >
<h5 class="text-caption q-mt-sm q-mb-none"> <h5 class="text-caption q-mt-sm q-mb-none">
Returns 201 CREATED (application/json) Returns 201 CREATED (application/json)
@ -62,8 +62,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5> <h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code <code
>curl -X POST {{ request.base_url }}api/v1/payments -d '{"out": false, >curl -X POST {{ request.base_url }}api/v1/payments -d '{"out": false,
"amount": &lt;int&gt;, "memo": &lt;string&gt;, "webhook": "amount": &lt;int&gt;, "memo": &lt;string&gt;}' -H "X-Api-Key:
&lt;url:string&gt;, "unit": &lt;string&gt;}' -H "X-Api-Key:
<i>{{ wallet.inkey }}</i>" -H "Content-type: application/json"</code <i>{{ wallet.inkey }}</i>" -H "Content-type: application/json"</code
> >
</q-card-section> </q-card-section>

View File

@ -144,6 +144,7 @@ class CreateInvoiceData(BaseModel):
unit: Optional[str] = "sat" unit: Optional[str] = "sat"
description_hash: Optional[str] = None description_hash: Optional[str] = None
unhashed_description: Optional[str] = None unhashed_description: Optional[str] = None
expiry: Optional[int] = None
lnurl_callback: Optional[str] = None lnurl_callback: Optional[str] = None
lnurl_balance_check: Optional[str] = None lnurl_balance_check: Optional[str] = None
extra: Optional[dict] = None extra: Optional[dict] = None
@ -189,6 +190,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
memo=memo, memo=memo,
description_hash=description_hash, description_hash=description_hash,
unhashed_description=unhashed_description, unhashed_description=unhashed_description,
expiry=data.expiry,
extra=data.extra, extra=data.extra,
webhook=data.webhook, webhook=data.webhook,
internal=data.internal, internal=data.internal,

View File

@ -167,6 +167,10 @@ class BoltzExtensionSettings(LNbitsSettings):
boltz_mempool_space_url_ws: str = Field(default="wss://mempool.space") boltz_mempool_space_url_ws: str = Field(default="wss://mempool.space")
class LightningSettings(LNbitsSettings):
lightning_invoice_expiry: int = Field(default=600)
class FundingSourcesSettings( class FundingSourcesSettings(
FakeWalletFundingSource, FakeWalletFundingSource,
LNbitsFundingSource, LNbitsFundingSource,
@ -191,6 +195,7 @@ class EditableSettings(
OpsSettings, OpsSettings,
FundingSourcesSettings, FundingSourcesSettings,
BoltzExtensionSettings, BoltzExtensionSettings,
LightningSettings,
): ):
@validator( @validator(
"lnbits_admin_users", "lnbits_admin_users",
@ -236,14 +241,14 @@ class SuperUserSettings(LNbitsSettings):
default=[ default=[
"VoidWallet", "VoidWallet",
"FakeWallet", "FakeWallet",
"CLightningWallet", "CoreLightningWallet",
"LndRestWallet", "LndRestWallet",
"EclairWallet",
"LndWallet", "LndWallet",
"LntxbotWallet", "LnTipsWallet",
"LNPayWallet", "LNPayWallet",
"LNbitsWallet", "LNbitsWallet",
"OpenNodeWallet", "OpenNodeWallet",
"LnTipsWallet",
] ]
) )
@ -261,7 +266,10 @@ class TransientSettings(InstalledExtensionsSettings):
class ReadOnlySettings( class ReadOnlySettings(
EnvSettings, SaaSSettings, PersistenceSettings, SuperUserSettings EnvSettings,
SaaSSettings,
PersistenceSettings,
SuperUserSettings,
): ):
lnbits_admin_ui: bool = Field(default=False) lnbits_admin_ui: bool = Field(default=False)

View File

@ -48,6 +48,7 @@ class ClicheWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
if unhashed_description or description_hash: if unhashed_description or description_hash:
description_hash_str = ( description_hash_str = (

View File

@ -83,6 +83,7 @@ class CoreLightningWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
label = "lbl{}".format(random.random()) label = "lbl{}".format(random.random())
msat: int = int(amount * 1000) msat: int = int(amount * 1000)
@ -103,6 +104,7 @@ class CoreLightningWallet(Wallet):
deschashonly=True deschashonly=True
if unhashed_description if unhashed_description
else False, # we can't pass None here else False, # we can't pass None here
expiry=kwargs.get("expiry"),
) )
if r.get("code") and r.get("code") < 0: if r.get("code") and r.get("code") < 0:

View File

@ -73,13 +73,17 @@ class EclairWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"amountMsat": amount * 1000} data: Dict = {"amountMsat": amount * 1000}
if kwargs.get("expiry"):
data["expireIn"] = kwargs["expiry"]
if description_hash: if description_hash:
data["description_hash"] = description_hash.hex() data["descriptionHash"] = description_hash.hex()
elif unhashed_description: elif unhashed_description:
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest() data["descriptionHash"] = hashlib.sha256(unhashed_description).hexdigest()
else: else:
data["description"] = memo or "" data["description"] = memo or ""
@ -162,53 +166,62 @@ class EclairWallet(Wallet):
) )
async def get_invoice_status(self, checking_id: str) -> PaymentStatus: async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
async with httpx.AsyncClient() as client: try:
r = await client.post( async with httpx.AsyncClient() as client:
f"{self.url}/getreceivedinfo", r = await client.post(
headers=self.auth, f"{self.url}/getreceivedinfo",
data={"paymentHash": checking_id}, headers=self.auth,
) data={"paymentHash": checking_id},
data = r.json() )
if r.is_error or "error" in data or data.get("status") is None: r.raise_for_status()
data = r.json()
if r.is_error or "error" in data or data.get("status") is None:
raise Exception("error in eclair response")
statuses = {
"received": True,
"expired": False,
"pending": None,
}
return PaymentStatus(statuses.get(data["status"]["type"]))
except:
return PaymentStatus(None) return PaymentStatus(None)
statuses = {
"received": True,
"expired": False,
"pending": None,
}
return PaymentStatus(statuses.get(data["status"]["type"]))
async def get_payment_status(self, checking_id: str) -> PaymentStatus: async def get_payment_status(self, checking_id: str) -> PaymentStatus:
async with httpx.AsyncClient() as client: try:
r = await client.post( async with httpx.AsyncClient() as client:
f"{self.url}/getsentinfo", r = await client.post(
headers=self.auth, f"{self.url}/getsentinfo",
data={"paymentHash": checking_id}, headers=self.auth,
timeout=40, data={"paymentHash": checking_id},
timeout=40,
)
r.raise_for_status()
data = r.json()[-1]
if r.is_error or "error" in data or data.get("status") is None:
raise Exception("error in eclair response")
fee_msat, preimage = None, None
if data["status"]["type"] == "sent":
fee_msat = -data["status"]["feesPaid"]
preimage = data["status"]["paymentPreimage"]
statuses = {
"sent": True,
"failed": False,
"pending": None,
}
return PaymentStatus(
statuses.get(data["status"]["type"]), fee_msat, preimage
) )
except:
if r.is_error:
return PaymentStatus(None) return PaymentStatus(None)
data = r.json()[-1]
if r.is_error or "error" in data or data.get("status") is None:
return PaymentStatus(None)
fee_msat, preimage = None, None
if data["status"]["type"] == "sent":
fee_msat = -data["status"]["feesPaid"]
preimage = data["status"]["paymentPreimage"]
statuses = {
"sent": True,
"failed": False,
"pending": None,
}
return PaymentStatus(statuses.get(data["status"]["type"]), fee_msat, preimage)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True: while True:
try: try:

View File

@ -41,6 +41,7 @@ class FakeWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = { data: Dict = {
"out": False, "out": False,
@ -54,6 +55,7 @@ class FakeWallet(Wallet):
"expires": None, "expires": None,
"route": None, "route": None,
} }
data["expires"] = kwargs.get("expiry")
data["amount"] = amount * 1000 data["amount"] = amount * 1000
data["timestamp"] = datetime.now().timestamp() data["timestamp"] = datetime.now().timestamp()
if description_hash: if description_hash:

View File

@ -59,8 +59,11 @@ class LNbitsWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"out": False, "amount": amount} data: Dict = {"out": False, "amount": amount}
if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
data["description_hash"] = description_hash.hex() data["description_hash"] = description_hash.hex()
if unhashed_description: if unhashed_description:

View File

@ -154,8 +154,11 @@ class LndWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
params: Dict = {"value": amount, "expiry": 600, "private": True} params: Dict = {"value": amount, "private": True}
if kwargs.get("expiry"):
params["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
params["description_hash"] = description_hash params["description_hash"] = description_hash
elif unhashed_description: elif unhashed_description:

View File

@ -77,6 +77,8 @@ class LndRestWallet(Wallet):
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"value": amount, "private": True} data: Dict = {"value": amount, "private": True}
if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
data["description_hash"] = base64.b64encode(description_hash).decode( data["description_hash"] = base64.b64encode(description_hash).decode(
"ascii" "ascii"

View File

@ -119,6 +119,7 @@ class SparkWallet(Wallet):
label=label, label=label,
description=memo or "", description=memo or "",
exposeprivatechannels=True, exposeprivatechannels=True,
expiry=kwargs.get("expiry"),
) )
ok, payment_request, error_message = True, r["bolt11"], "" ok, payment_request, error_message = True, r["bolt11"], ""
except (SparkError, UnknownError) as e: except (SparkError, UnknownError) as e:

118
poetry.lock generated
View File

@ -177,14 +177,14 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]] [[package]]
name = "boltz-client" name = "boltz-client"
version = "0.1.2" version = "0.1.3"
description = "python boltz client" description = "python boltz client"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
files = [ files = [
{file = "boltz_client-0.1.2-py3-none-any.whl", hash = "sha256:2fb0814c7c3ea88d039e71088648df27db0c036b777b0618bd30638dd76ebe90"}, {file = "boltz_client-0.1.3-py3-none-any.whl", hash = "sha256:67a231de6cc0876376e32aa177eb572ef14e869a645ef4565f307daa527f1e76"},
{file = "boltz_client-0.1.2.tar.gz", hash = "sha256:b360c0ff26f2dea62af6457de4d8c46e434cd24b607ed3aa71494409b57e082b"}, {file = "boltz_client-0.1.3.tar.gz", hash = "sha256:1573be84ea547578591d78f5f1ea65ccda01edcca0aa6f077bcd5a497622cef5"},
] ]
[package.dependencies] [package.dependencies]
@ -451,63 +451,63 @@ files = [
[[package]] [[package]]
name = "coverage" name = "coverage"
version = "7.0.5" version = "7.1.0"
description = "Code coverage measurement for Python" description = "Code coverage measurement for Python"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"}, {file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"},
{file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"}, {file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"}, {file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"}, {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"}, {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"}, {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"}, {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"}, {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"},
{file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"}, {file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"},
{file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"}, {file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"},
{file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"}, {file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"},
{file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"}, {file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"}, {file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"}, {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"}, {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"}, {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"}, {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"}, {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"},
{file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"}, {file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"},
{file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"}, {file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"},
{file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"}, {file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"}, {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"}, {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"}, {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"}, {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"}, {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"}, {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"},
{file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"}, {file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"},
{file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"}, {file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"},
{file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"}, {file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"},
{file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"}, {file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"}, {file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"}, {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"}, {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"}, {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"}, {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"}, {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"},
{file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"}, {file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"},
{file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"}, {file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"},
{file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"}, {file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"},
{file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"}, {file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"}, {file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"}, {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"}, {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"}, {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"}, {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"}, {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"},
{file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"}, {file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"},
{file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"}, {file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"},
{file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"}, {file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"},
{file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"}, {file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"},
] ]
[package.dependencies] [package.dependencies]
@ -1156,14 +1156,14 @@ six = "*"
[[package]] [[package]]
name = "pathspec" name = "pathspec"
version = "0.10.3" version = "0.11.0"
description = "Utility library for gitignore style pattern matching of file paths." description = "Utility library for gitignore style pattern matching of file paths."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"},
{file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
] ]
[[package]] [[package]]
@ -2112,4 +2112,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7" python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
content-hash = "b2d22a2a33b4c0a4491b5519b28772435c15747b407a150ffa591bcf6ccb56a6" content-hash = "5d0d9d187fe3c23840fab1d85fca5f0c166e430c4f5d1c1ff636cc621e339eba"

View File

@ -62,8 +62,8 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4" Cerberus = "^1.3.4"
async-timeout = "^4.0.2" async-timeout = "^4.0.2"
pyln-client = "0.11.1" pyln-client = "0.11.1"
cashu = "^0.8.2" cashu = "0.8.2"
boltz-client = "^0.1.2" boltz-client = "0.1.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]

View File

@ -94,6 +94,21 @@ async def test_create_internal_invoice(client, inkey_headers_to):
return invoice return invoice
# check POST /api/v1/payments: invoice with custom expiry
@pytest.mark.asyncio
async def test_create_invoice_custom_expiry(client, inkey_headers_to):
data = await get_random_invoice_data()
expiry_seconds = 600 * 6 * 24 * 31 # 31 days in the future
data["expiry"] = expiry_seconds
response = await client.post(
"/api/v1/payments", json=data, headers=inkey_headers_to
)
assert response.status_code == 201
invoice = response.json()
bolt11_invoice = bolt11.decode(invoice["payment_request"])
assert bolt11_invoice.expiry == expiry_seconds
# check POST /api/v1/payments: make payment # check POST /api/v1/payments: make payment
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_pay_invoice(client, invoice, adminkey_headers_from): async def test_pay_invoice(client, invoice, adminkey_headers_from):

View File

@ -0,0 +1,80 @@
# Python script to create a fake admin user for sqlite3,
# for regtest setup as LNbits funding source
import os
import sqlite3
import sys
import time
from uuid import uuid4
import shortuuid
adminkey = "d08a3313322a4514af75d488bcc27eee"
sqfolder = "./data"
if not sqfolder or not os.path.isdir(sqfolder):
print("missing LNBITS_DATA_FOLDER")
sys.exit(1)
file = os.path.join(sqfolder, "database.sqlite3")
conn = sqlite3.connect(file)
cursor = conn.cursor()
old_account = cursor.execute(
"SELECT * FROM accounts WHERE id = ?", (adminkey,)
).fetchone()
if old_account:
print("fake admin does already exist")
sys.exit(1)
cursor.execute("INSERT INTO accounts (id) VALUES (?)", (adminkey,))
wallet_id = uuid4().hex
cursor.execute(
"""
INSERT INTO wallets (id, name, "user", adminkey, inkey)
VALUES (?, ?, ?, ?, ?)
""",
(
wallet_id,
"TEST WALLET",
adminkey,
adminkey,
uuid4().hex, # invoice key is not important
),
)
expiration_date = time.time() + 420
# 1 btc in sats
amount = 100_000_000
internal_id = f"internal_{shortuuid.uuid()}"
cursor.execute(
"""
INSERT INTO apipayments
(wallet, checking_id, bolt11, hash, preimage,
amount, pending, memo, fee, extra, webhook, expiry)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
wallet_id,
internal_id,
"test_admin_internal",
"test_admin_internal",
None,
amount * 1000,
False,
"test_admin_internal",
0,
None,
"",
expiration_date,
),
)
print(f"created test admin: {adminkey} with {amount} sats")
conn.commit()
cursor.close()