Mint: Add LndRest and regtest tests (#359)

* update

* working

* test with lnd

* update action

* cache poetry

* add lndrest

* enable regtest

* add regtests.yml

* poetry version

* add helpers

* save

* run legend regtest fork

* actually start

* use bash

* give rights

* remove cache?

* change order

* tests succeed with lndrestwallet

* check if wallet is set

* settings for regtest

* fix fakewallet test

* remove wacky balance check

* adjust permissions

* try with sudo

* adjust example

* remove eclair
This commit is contained in:
callebtc
2023-11-14 21:46:06 -03:00
committed by GitHub
parent 286f850de8
commit 45d3059c2d
21 changed files with 816 additions and 63 deletions

View File

@@ -44,13 +44,18 @@ MINT_DERIVATION_PATH="0/0/0/0"
MINT_DATABASE=data/mint
# Lightning
# Supported: LNbitsWallet, FakeWallet
# Supported: LndRestWallet, LNbitsWallet, FakeWallet
MINT_LIGHTNING_BACKEND=LNbitsWallet
# for use with LNbitsWallet
MINT_LNBITS_ENDPOINT=https://legend.lnbits.com
MINT_LNBITS_KEY=yourkeyasdasdasd
# LndRestWallet
MINT_LND_REST_ENDPOINT=https://127.0.0.1:8086
MINT_LND_REST_CERT="/home/lnd/.lnd/tls.cert"
MINT_LND_REST_MACAROON="/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon"
# fee to reserve in percent of the amount
LIGHTNING_FEE_PERCENT=1.0
# minimum fee to reserve

27
.github/actions/prepare/action.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: prepare
inputs:
python-version:
description: "Python Version"
required: true
default: "3.10"
poetry-version:
description: "Poetry Version"
default: "1.5.1"
runs:
using: "composite"
steps:
- name: Set up Poetry ${{ inputs.poetry-version }}
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ inputs.poetry-version }}
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}
cache: "poetry"
- name: Install dependencies
run: |
poetry install --extras pgsql
shell: bash

View File

@@ -1,24 +1,28 @@
name: checks
on: [push, pull_request]
on:
workflow_call:
inputs:
python-version:
default: "3.10.4"
type: string
poetry-version:
default: "1.5.1"
type: string
jobs:
formatting:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10.4"]
poetry-version: ["1.5.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Poetry ${{ matrix.poetry-version }}
- name: Set up Poetry ${{ inputs.poetry-version }}
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Set up Python ${{ matrix.python-version }}
poetry-version: ${{ inputs.poetry-version }}
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ inputs.python-version }}
cache: "poetry"
- name: Install packages
run: poetry install
@@ -26,20 +30,16 @@ jobs:
run: make black-check
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10.4"]
poetry-version: ["1.5.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Poetry ${{ matrix.poetry-version }}
- name: Set up Poetry ${{ inputs.poetry-version }}
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Set up Python ${{ matrix.python-version }}
poetry-version: ${{ inputs.poetry-version }}
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ inputs.python-version }}
cache: "poetry"
- name: Install packages
run: poetry install

33
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Nutshell CI
on:
push:
branches:
- main
pull_request:
jobs:
checks:
uses: ./.github/workflows/checks.yml
tests:
strategy:
matrix:
os: [ubuntu-latest]
python-version: ["3.9", "3.10"]
poetry-version: ["1.5.1"]
# db-url: ["", "postgres://cashu:cashu@localhost:5432/test"] # TODO: Postgres test not working
db-url: [""]
backend-wallet-class: ["FakeWallet"]
uses: ./.github/workflows/tests.yml
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ matrix.poetry-version }}
regtest:
uses: ./.github/workflows/regtest.yml
strategy:
matrix:
python-version: ["3.10"]
poetry-version: ["1.5.1"]
backend-wallet-class: ["LndRestWallet", "LNbitsWallet"]
with:
python-version: ${{ matrix.python-version }}
backend-wallet-class: ${{ matrix.backend-wallet-class }}

74
.github/workflows/regtest.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: regtest
on:
workflow_call:
inputs:
python-version:
default: "3.10.4"
type: string
poetry-version:
default: "1.5.1"
type: string
os-version:
default: "ubuntu-latest"
type: string
db-url:
default: ""
type: string
backend-wallet-class:
required: true
type: string
jobs:
regtest:
runs-on: ${{ inputs.os-version }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/prepare
with:
python-version: ${{ inputs.python-version }}
poetry-version: ${{ inputs.poetry-version }}
- name: Setup Regtest
run: |
git clone https://github.com/callebtc/cashu-regtest-enviroment.git regtest
cd regtest
chmod -R 777 .
bash ./start.sh
- name: Create fake admin
if: ${{ inputs.backend-wallet-class == 'LNbitsWallet' }}
run: docker exec cashu-lnbits-1 poetry run python tools/create_fake_admin.py
- name: Run Tests
env:
WALLET_NAME: test_wallet
MINT_HOST: localhost
MINT_PORT: 3337
MINT_DATABASE: ${{ inputs.db-url }}
TOR: false
MINT_LIGHTNING_BACKEND: ${{ inputs.backend-wallet-class }}
MINT_LNBITS_ENDPOINT: http://localhost:5001
MINT_LNBITS_KEY: d08a3313322a4514af75d488bcc27eee
MINT_LND_REST_ENDPOINT: https://localhost:8081/
MINT_LND_REST_CERT: ./regtest/data/lnd-3/tls.cert
MINT_LND_REST_MACAROON: ./regtest/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon
# LND_GRPC_ENDPOINT: localhost
# LND_GRPC_PORT: 10009
# LND_GRPC_CERT: docker/data/lnd-3/tls.cert
# LND_GRPC_MACAROON: docker/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon
# CORELIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc
# CORELIGHTNING_REST_URL: https://localhost:3001
# CORELIGHTNING_REST_MACAROON: ./docker/data/clightning-2-rest/access.macaroon
# CORELIGHTNING_REST_CERT: ./docker/data/clightning-2-rest/certificate.pem
run: |
sudo chmod -R 777 .
make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml

View File

@@ -1,10 +1,24 @@
name: tests
on: [push, pull_request]
on:
workflow_call:
inputs:
python-version:
default: "3.10.4"
type: string
poetry-version:
default: "1.5.1"
type: string
db-url:
default: ""
type: string
os:
default: "ubuntu-latest"
type: string
jobs:
poetry:
runs-on: ${{ matrix.os }}
runs-on: ${{ inputs.os }}
services:
postgres:
image: postgres:latest
@@ -19,34 +33,16 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
matrix:
os: [ubuntu-latest]
python-version: ["3.10.4"]
poetry-version: ["1.5.1"]
# db-url: ["", "postgres://cashu:cashu@localhost:5432/test"] # TODO: Postgres test not working
db-url: [""]
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v2
- uses: ./.github/actions/prepare
with:
submodules: recursive
- name: Set up Poetry ${{ matrix.poetry-version }}
uses: abatilo/actions-poetry@v2
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
- name: Install dependencies
run: |
poetry install --extras pgsql
shell: bash
python-version: ${{ inputs.python-version }}
poetry-version: ${{ inputs.poetry-version }}
- name: Run tests
env:
LIGHTNING: false
MINT_LIGHTNING_BACKEND: FakeWallet
WALLET_NAME: test_wallet
MINT_HOST: localhost
MINT_PORT: 3337

View File

@@ -29,8 +29,19 @@ package:
python setup.py sdist bdist_wheel
test:
PYTHONUNBUFFERED=1 \
DEBUG=true \
poetry run pytest tests --cov-report xml --cov cashu
test-lndrest:
PYTHONUNBUFFERED=1 \
DEBUG=true \
MINT_LIGHTNING_BACKEND=LndRestWallet \
MINT_LND_REST_ENDPOINT=https://localhost:8081/ \
MINT_LND_REST_CERT=../cashu-regtest-enviroment/data/lnd-3/tls.cert \
MINT_LND_REST_MACAROON=../cashu-regtest-enviroment/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon \
poetry run pytest tests/test_cli.py --cov-report xml --cov cashu
install:
make clean
python setup.py sdist bdist_wheel

View File

@@ -1,7 +1,7 @@
import os
import sys
from pathlib import Path
from typing import List
from typing import List, Optional
from environs import Env # type: ignore
from pydantic import BaseSettings, Extra, Field
@@ -99,8 +99,17 @@ class WalletSettings(CashuSettings):
locktime_delta_seconds: int = Field(default=86400) # 1 day
class LndRestFundingSource(MintSettings):
mint_lnd_rest_endpoint: Optional[str] = Field(default=None)
mint_lnd_rest_cert: Optional[str] = Field(default=None)
mint_lnd_rest_macaroon: Optional[str] = Field(default=None)
mint_lnd_rest_admin_macaroon: Optional[str] = Field(default=None)
mint_lnd_rest_invoice_macaroon: Optional[str] = Field(default=None)
class Settings(
EnvSettings,
LndRestFundingSource,
MintSettings,
MintInformation,
WalletSettings,

View File

@@ -1,3 +1,8 @@
# type: ignore
from ..core.settings import settings
from .fake import FakeWallet # noqa: F401
from .lnbits import LNbitsWallet # noqa: F401
from .lndrest import LndRestWallet # noqa: F401
if settings.mint_lightning_backend is None:
raise Exception("MINT_LIGHTNING_BACKEND not configured")

271
cashu/lightning/lndrest.py Normal file
View File

@@ -0,0 +1,271 @@
import asyncio
import base64
import hashlib
import json
from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
from ..core.settings import settings
from .base import (
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
def load_macaroon(macaroon: str) -> str:
"""Returns hex version of a macaroon encoded in base64 or the file path.
:param macaroon: Macaroon encoded in base64 or file path.
:type macaroon: str
:return: Hex version of macaroon.
:rtype: str
"""
# if the macaroon is a file path, load it and return hex version
if macaroon.split(".")[-1] == "macaroon":
with open(macaroon, "rb") as f:
macaroon_bytes = f.read()
return macaroon_bytes.hex()
else:
# if macaroon is a provided string
# check if it is hex, if so, return
try:
bytes.fromhex(macaroon)
return macaroon
except ValueError:
pass
# convert the bas64 macaroon to hex
try:
macaroon = base64.b64decode(macaroon).hex()
except Exception:
pass
return macaroon
class LndRestWallet(Wallet):
"""https://api.lightning.community/rest/index.html#lnd-rest-api-reference"""
def __init__(self):
endpoint = settings.mint_lnd_rest_endpoint
cert = settings.mint_lnd_rest_cert
macaroon = (
settings.mint_lnd_rest_macaroon
or settings.mint_lnd_rest_admin_macaroon
or settings.mint_lnd_rest_invoice_macaroon
)
if not endpoint:
raise Exception("cannot initialize lndrest: no endpoint")
if not macaroon:
raise Exception("cannot initialize lndrest: no macaroon")
if not cert:
logger.warning(
"no certificate for lndrest provided, this only works if you have a"
" publicly issued certificate"
)
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
endpoint = (
f"https://{endpoint}" if not endpoint.startswith("http") else endpoint
)
self.endpoint = endpoint
self.macaroon = load_macaroon(macaroon)
# if no cert provided it should be public so we set verify to True
# and it will still check for validity of certificate and fail if its not valid
# even on startup
self.cert = cert or True
self.auth = {"Grpc-Metadata-macaroon": self.macaroon}
self.client = httpx.AsyncClient(
base_url=self.endpoint, headers=self.auth, verify=self.cert
)
async def status(self) -> StatusResponse:
try:
r = await self.client.get("/v1/balance/channels")
r.raise_for_status()
except (httpx.ConnectError, httpx.RequestError) as exc:
return StatusResponse(
error_message=f"Unable to connect to {self.endpoint}. {exc}",
balance_msat=0,
)
try:
data = r.json()
if r.is_error:
raise Exception
except Exception:
return StatusResponse(error_message=r.text[:200], balance_msat=0)
return StatusResponse(
error_message=None, balance_msat=int(data["balance"]) * 1000
)
async def create_invoice(
self,
amount: int,
memo: Optional[str] = None,
description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse:
data: Dict = {"value": amount, "private": True, "memo": memo or ""}
if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"]
if description_hash:
data["description_hash"] = base64.b64encode(description_hash).decode(
"ascii"
)
elif unhashed_description:
data["description_hash"] = base64.b64encode(
hashlib.sha256(unhashed_description).digest()
).decode("ascii")
r = await self.client.post(url="/v1/invoices", json=data)
if r.is_error:
error_message = r.text
try:
error_message = r.json()["error"]
except Exception:
pass
return InvoiceResponse(
ok=False,
checking_id=None,
payment_request=None,
error_message=error_message,
)
data = r.json()
payment_request = data["payment_request"]
payment_hash = base64.b64decode(data["r_hash"]).hex()
checking_id = payment_hash
return InvoiceResponse(
ok=True,
checking_id=checking_id,
payment_request=payment_request,
error_message=None,
)
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
# set the fee limit for the payment
lnrpcFeeLimit = dict()
lnrpcFeeLimit["fixed_msat"] = f"{fee_limit_msat}"
r = await self.client.post(
url="/v1/channels/transactions",
json={"payment_request": bolt11, "fee_limit": lnrpcFeeLimit},
timeout=None,
)
if r.is_error or r.json().get("payment_error"):
error_message = r.json().get("payment_error") or r.text
return PaymentResponse(
ok=False,
checking_id=None,
fee_msat=None,
preimage=None,
error_message=error_message,
)
data = r.json()
checking_id = base64.b64decode(data["payment_hash"]).hex()
fee_msat = int(data["payment_route"]["total_fees_msat"])
preimage = base64.b64decode(data["payment_preimage"]).hex()
return PaymentResponse(
ok=True,
checking_id=checking_id,
fee_msat=fee_msat,
preimage=preimage,
error_message=None,
)
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = await self.client.get(url=f"/v1/invoice/{checking_id}")
if r.is_error or not r.json().get("settled"):
# this must also work when checking_id is not a hex recognizable by lnd
# it will return an error and no "settled" attribute on the object
return PaymentStatus(paid=None)
return PaymentStatus(paid=True)
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
"""
This routine checks the payment status using routerpc.TrackPaymentV2.
"""
# convert checking_id from hex to base64 and some LND magic
try:
checking_id = base64.urlsafe_b64encode(bytes.fromhex(checking_id)).decode(
"ascii"
)
except ValueError:
return PaymentStatus(paid=None)
url = f"/v2/router/track/{checking_id}"
# check payment.status:
# https://api.lightning.community/?python=#paymentpaymentstatus
statuses = {
"UNKNOWN": None,
"IN_FLIGHT": None,
"SUCCEEDED": True,
"FAILED": False,
}
async with self.client.stream("GET", url, timeout=None) as r:
async for json_line in r.aiter_lines():
try:
line = json.loads(json_line)
if line.get("error"):
logger.error(
line["error"]["message"]
if "message" in line["error"]
else line["error"]
)
return PaymentStatus(paid=None)
payment = line.get("result")
if payment is not None and payment.get("status"):
return PaymentStatus(
paid=statuses[payment["status"]],
fee_msat=payment.get("fee_msat"),
preimage=payment.get("payment_preimage"),
)
else:
return PaymentStatus(paid=None)
except Exception:
continue
return PaymentStatus(paid=None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True:
try:
url = "/v1/invoices/subscribe"
async with self.client.stream("GET", url, timeout=None) as r:
async for line in r.aiter_lines():
try:
inv = json.loads(line)["result"]
if not inv["settled"]:
continue
except Exception:
continue
payment_hash = base64.b64decode(inv["r_hash"]).hex()
yield payment_hash
except Exception as exc:
logger.error(
f"lost connection to lnd invoices stream: '{exc}', retrying in 5"
" seconds"
)
await asyncio.sleep(5)

View File

@@ -210,9 +210,17 @@ async def pay(ctx: Context, invoice: str, yes: bool):
help="Split minted tokens with a specific amount.",
type=int,
)
@click.option(
"--no-check",
"-n",
default=False,
is_flag=True,
help="Do not check if invoice is paid.",
type=bool,
)
@click.pass_context
@coro
async def invoice(ctx: Context, amount: int, id: str, split: int):
async def invoice(ctx: Context, amount: int, id: str, split: int, no_check: bool):
wallet: Wallet = ctx.obj["WALLET"]
await wallet.load_mint()
wallet.status()
@@ -236,9 +244,11 @@ async def invoice(ctx: Context, amount: int, id: str, split: int):
print(f"Invoice: {invoice.bolt11}")
print("")
print(
"If you abort this you can use this command to recheck the"
f" invoice:\ncashu invoice {amount} --id {invoice.id}"
"You can use this command to check the invoice: cashu invoice"
f" {amount} --id {invoice.id}"
)
if no_check:
return
check_until = time.time() + 5 * 60 # check for five minutes
print("")
print(

0
tests/__init__.py Normal file
View File

View File

@@ -28,7 +28,7 @@ settings.mint_listen_port = SERVER_PORT
settings.mint_url = SERVER_ENDPOINT
settings.lightning = True
settings.tor = False
settings.mint_lightning_backend = "FakeWallet"
settings.mint_lightning_backend = settings.mint_lightning_backend or "FakeWallet"
settings.mint_database = "./test_data/test_mint"
settings.mint_derivation_path = "0/0/0/0"
settings.mint_private_key = "TEST_PRIVATE_KEY"

184
tests/helpers.py Normal file
View File

@@ -0,0 +1,184 @@
import hashlib
import importlib
import json
import os
import random
import string
import time
from subprocess import PIPE, Popen, TimeoutExpired
from typing import Tuple
from loguru import logger
from cashu.core.settings import settings
def get_random_string(N: int = 10):
return "".join(
random.SystemRandom().choice(string.ascii_uppercase + string.digits)
for _ in range(N)
)
async def get_random_invoice_data():
return {"out": False, "amount": 10, "memo": f"test_memo_{get_random_string(10)}"}
wallets_module = importlib.import_module("cashu.lightning")
wallet_class = getattr(wallets_module, settings.mint_lightning_backend)
WALLET = wallet_class()
is_fake: bool = WALLET.__class__.__name__ == "FakeWallet"
is_regtest: bool = not is_fake
docker_lightning_cli = [
"docker",
"exec",
"cashu-lnd-1-1",
"lncli",
"--network",
"regtest",
"--rpcserver=lnd-1",
]
docker_bitcoin_cli = [
"docker",
"exec",
"cashu-bitcoind-1-1bitcoin-cli",
"-rpcuser=lnbits",
"-rpcpassword=lnbits",
"-regtest",
]
docker_lightning_unconnected_cli = [
"docker",
"exec",
"cashu-lnd-2-1",
"lncli",
"--network",
"regtest",
"--rpcserver=lnd-2",
]
def run_cmd(cmd: list) -> str:
timeout = 20
process = Popen(cmd, stdout=PIPE, stderr=PIPE)
def process_communication(comm):
stdout, stderr = comm
output = stdout.decode("utf-8").strip()
error = stderr.decode("utf-8").strip()
return output, error
try:
now = time.time()
output, error = process_communication(process.communicate(timeout=timeout))
took = time.time() - now
logger.debug(f"ran command output: {output}, error: {error}, took: {took}s")
return output
except TimeoutExpired:
process.kill()
output, error = process_communication(process.communicate())
logger.error(f"timeout command: {cmd}, output: {output}, error: {error}")
raise
def run_cmd_json(cmd: list) -> dict:
output = run_cmd(cmd)
try:
return json.loads(output) if output else {}
except json.decoder.JSONDecodeError:
logger.error(f"failed to decode json from cmd `{cmd}`: {output}")
raise
def get_hold_invoice(sats: int) -> Tuple[str, dict]:
preimage = os.urandom(32)
preimage_hash = hashlib.sha256(preimage).hexdigest()
cmd = docker_lightning_cli.copy()
cmd.extend(["addholdinvoice", preimage_hash, str(sats)])
json = run_cmd_json(cmd)
return preimage.hex(), json
def settle_invoice(preimage: str) -> str:
cmd = docker_lightning_cli.copy()
cmd.extend(["settleinvoice", preimage])
return run_cmd(cmd)
def cancel_invoice(preimage_hash: str) -> str:
cmd = docker_lightning_cli.copy()
cmd.extend(["cancelinvoice", preimage_hash])
return run_cmd(cmd)
def get_real_invoice(sats: int) -> dict:
cmd = docker_lightning_cli.copy()
cmd.extend(["addinvoice", str(sats)])
return run_cmd_json(cmd)
def pay_real_invoice(invoice: str) -> str:
cmd = docker_lightning_cli.copy()
cmd.extend(["payinvoice", "--force", invoice])
return run_cmd(cmd)
def mine_blocks(blocks: int = 1) -> str:
cmd = docker_bitcoin_cli.copy()
cmd.extend(["-generate", str(blocks)])
return run_cmd(cmd)
def get_unconnected_node_uri() -> str:
cmd = docker_lightning_unconnected_cli.copy()
cmd.append("getinfo")
info = run_cmd_json(cmd)
pubkey = info["identity_pubkey"]
return f"{pubkey}@lnd-2:9735"
def create_onchain_address(address_type: str = "bech32") -> str:
cmd = docker_bitcoin_cli.copy()
cmd.extend(["getnewaddress", address_type])
return run_cmd(cmd)
def pay_onchain(address: str, sats: int) -> str:
btc = sats * 0.00000001
cmd = docker_bitcoin_cli.copy()
cmd.extend(["sendtoaddress", address, str(btc)])
return run_cmd(cmd)
# def clean_database(settings):
# if DB_TYPE == POSTGRES:
# db_url = make_url(settings.lnbits_database_url)
# conn = psycopg2.connect(settings.lnbits_database_url)
# conn.autocommit = True
# with conn.cursor() as cur:
# try:
# cur.execute("DROP DATABASE lnbits_test")
# except psycopg2.errors.InvalidCatalogName:
# pass
# cur.execute("CREATE DATABASE lnbits_test")
# db_url.database = "lnbits_test"
# settings.lnbits_database_url = str(db_url)
# core.db.__init__("database")
# conn.close()
# else:
# # FIXME: do this once mock data is removed from test data folder
# # os.remove(settings.lnbits_data_folder + "/database.sqlite3")
# pass
def pay_if_regtest(bolt11: str):
if is_regtest:
pay_real_invoice(bolt11)

View File

@@ -7,6 +7,7 @@ from cashu.core.base import TokenV3
from cashu.core.settings import settings
from cashu.wallet.cli.cli import cli
from cashu.wallet.wallet import Wallet
from tests.helpers import is_fake, pay_if_regtest
@pytest.fixture(autouse=True, scope="session")
@@ -14,6 +15,16 @@ def cli_prefix():
yield ["--wallet", "test_cli_wallet", "--host", settings.mint_url, "--tests"]
def get_bolt11_and_invoice_id_from_invoice_command(output: str) -> (str, str):
invoice = [
line.split(" ")[1] for line in output.split("\n") if line.startswith("Invoice")
][0]
invoice_id = [
line.split(" ")[-1] for line in output.split("\n") if line.startswith("You can")
][0]
return invoice, invoice_id
async def init_wallet():
wallet = await Wallet.with_db(
url=settings.mint_host,
@@ -77,7 +88,8 @@ def test_balance(cli_prefix):
assert result.exit_code == 0
def test_invoice(mint, cli_prefix):
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
def test_invoice_automatic_fakewallet(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
cli,
@@ -87,20 +99,60 @@ def test_invoice(mint, cli_prefix):
print("INVOICE")
print(result.output)
wallet = asyncio.run(init_wallet())
# assert wallet.available_balance >= 1000
assert wallet.available_balance >= 1000
assert f"Balance: {wallet.available_balance} sat" in result.output
assert result.exit_code == 0
def test_invoice(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoice", "-n", "1000"],
)
assert result.exception is None
invoice, invoice_id = get_bolt11_and_invoice_id_from_invoice_command(result.output)
pay_if_regtest(invoice)
result = runner.invoke(
cli,
[*cli_prefix, "invoice", "1000", "--id", invoice_id],
)
assert result.exception is None
wallet = asyncio.run(init_wallet())
assert wallet.available_balance >= 1000
assert result.exit_code == 0
def test_invoice_with_split(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoice", "10", "-s", "1"],
[
*cli_prefix,
"invoice",
"10",
"-s",
"1",
"-n",
],
)
assert result.exception is None
# wallet = asyncio.run(init_wallet())
# assert wallet.proof_amounts.count(1) >= 10
invoice, invoice_id = get_bolt11_and_invoice_id_from_invoice_command(result.output)
pay_if_regtest(invoice)
result = runner.invoke(
cli,
[*cli_prefix, "invoice", "10", "-s", "1", "--id", invoice_id],
)
assert result.exception is None
assert result.exception is None
wallet = asyncio.run(init_wallet())
assert wallet.proof_amounts.count(1) >= 10
def test_wallets(cli_prefix):

View File

@@ -5,6 +5,7 @@ from cashu.mint.ledger import Ledger
from cashu.wallet.wallet import Wallet
from cashu.wallet.wallet import Wallet as Wallet1
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import pay_if_regtest
@pytest_asyncio.fixture(scope="function")
@@ -23,8 +24,10 @@ async def wallet1(mint):
async def test_melt(wallet1: Wallet, ledger: Ledger):
# mint twice so we have enough to pay the second invoice back
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
assert wallet1.balance == 128
total_amount, fee_reserve_sat = await wallet1.get_pay_amount_with_fees(
@@ -41,6 +44,7 @@ async def test_melt(wallet1: Wallet, ledger: Ledger):
@pytest.mark.asyncio
async def test_split(wallet1: Wallet, ledger: Ledger):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10)
@@ -57,6 +61,7 @@ async def test_split(wallet1: Wallet, ledger: Ledger):
@pytest.mark.asyncio
async def test_check_proof_state(wallet1: Wallet, ledger: Ledger):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10)

View File

@@ -14,6 +14,7 @@ from cashu.wallet.wallet import Wallet
from cashu.wallet.wallet import Wallet as Wallet1
from cashu.wallet.wallet import Wallet as Wallet2
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import get_real_invoice, is_regtest, pay_if_regtest
async def assert_err(f, msg: Union[str, CashuError]):
@@ -138,6 +139,7 @@ async def test_get_keyset_ids(wallet1: Wallet):
@pytest.mark.asyncio
async def test_mint(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
assert wallet1.balance == 64
@@ -158,6 +160,7 @@ async def test_mint(wallet1: Wallet):
async def test_mint_amounts(wallet1: Wallet):
"""Mint predefined amounts"""
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
amts = [1, 1, 1, 2, 2, 4, 16]
await wallet1.mint(amount=sum(amts), split=amts, id=invoice.id)
assert wallet1.balance == 27
@@ -187,6 +190,7 @@ async def test_mint_amounts_wrong_order(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
assert wallet1.balance == 64
p1, p2 = await wallet1.split(wallet1.proofs, 20)
@@ -202,6 +206,7 @@ async def test_split(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split_to_send(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
keep_proofs, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True
@@ -217,6 +222,7 @@ async def test_split_to_send(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split_more_than_balance(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await assert_err(
wallet1.split(wallet1.proofs, 128),
@@ -230,8 +236,10 @@ async def test_split_more_than_balance(wallet1: Wallet):
async def test_melt(wallet1: Wallet):
# mint twice so we have enough to pay the second invoice back
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
assert wallet1.balance == 128
@@ -243,38 +251,46 @@ async def test_melt(wallet1: Wallet):
assert fee_reserve_sat == 2
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, total_amount)
invoice_to_pay = invoice.bolt11
invoice_payment_hash = str(invoice.payment_hash)
if is_regtest:
invoice_dict = get_real_invoice(64)
invoice_to_pay = invoice_dict["payment_request"]
invoice_payment_hash = str(invoice_dict["r_hash"])
melt_response = await wallet1.pay_lightning(
send_proofs, invoice=invoice.bolt11, fee_reserve_sat=fee_reserve_sat
send_proofs, invoice=invoice_to_pay, fee_reserve_sat=fee_reserve_sat
)
assert melt_response.change
assert len(melt_response.change) == 1
assert melt_response.change, "No change returned"
assert len(melt_response.change) == 1, "More than one change returned"
# NOTE: we assume that we will get a token back from the same keyset as the ones we melted
# this could be wrong if we melted tokens from an old keyset but the returned ones are
# from a newer one.
assert melt_response.change[0].id == send_proofs[0].id
assert melt_response.change[0].id == send_proofs[0].id, "Wrong keyset returned"
# verify that proofs in proofs_used db have the same melt_id as the invoice in the db
assert invoice.payment_hash
assert invoice.payment_hash, "No payment hash in invoice"
invoice_db = await get_lightning_invoice(
db=wallet1.db, payment_hash=invoice.payment_hash, out=True
db=wallet1.db, payment_hash=invoice_payment_hash, out=True
)
assert invoice_db
assert invoice_db, "No invoice in db"
proofs_used = await get_proofs(
db=wallet1.db, melt_id=invoice_db.id, table="proofs_used"
)
assert len(proofs_used) == len(send_proofs)
assert all([p.melt_id == invoice_db.id for p in proofs_used])
assert len(proofs_used) == len(send_proofs), "Not all proofs used"
assert all([p.melt_id == invoice_db.id for p in proofs_used]), "Wrong melt_id"
# the payment was without fees so we need to remove it from the total amount
assert wallet1.balance == 128 - (total_amount - fee_reserve_sat)
assert wallet1.balance == 64
assert wallet1.balance == 128 - (total_amount - fee_reserve_sat), "Wrong balance"
assert wallet1.balance == 64, "Wrong balance"
@pytest.mark.asyncio
async def test_split_to_send_more_than_balance(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await assert_err(
wallet1.split_to_send(wallet1.proofs, 128, set_reserved=True),
@@ -287,6 +303,7 @@ async def test_split_to_send_more_than_balance(wallet1: Wallet):
@pytest.mark.asyncio
async def test_double_spend(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
doublespend = await wallet1.mint(64, id=invoice.id)
await wallet1.split(wallet1.proofs, 20)
await assert_err(
@@ -300,6 +317,7 @@ async def test_double_spend(wallet1: Wallet):
@pytest.mark.asyncio
async def test_duplicate_proofs_double_spent(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
doublespend = await wallet1.mint(64, id=invoice.id)
await assert_err(
wallet1.split(wallet1.proofs + doublespend, 20),
@@ -312,6 +330,7 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet):
@pytest.mark.asyncio
async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
_, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True
@@ -330,6 +349,7 @@ async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
async def test_invalidate_unspent_proofs(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet. Should not work!"""
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await wallet1.invalidate(wallet1.proofs)
assert wallet1.balance == 64
@@ -339,6 +359,7 @@ async def test_invalidate_unspent_proofs(wallet1: Wallet):
async def test_invalidate_unspent_proofs_without_checking(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet but force no check."""
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await wallet1.invalidate(wallet1.proofs, check_spendable=False)
assert wallet1.balance == 0
@@ -347,6 +368,7 @@ async def test_invalidate_unspent_proofs_without_checking(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split_invalid_amount(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await assert_err(
wallet1.split(wallet1.proofs, -1),
@@ -357,6 +379,7 @@ async def test_split_invalid_amount(wallet1: Wallet):
@pytest.mark.asyncio
async def test_token_state(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
assert wallet1.balance == 64
resp = await wallet1.check_proof_state(wallet1.proofs)

View File

@@ -8,6 +8,7 @@ from cashu.lightning.base import InvoiceResponse, PaymentStatus
from cashu.wallet.api.app import app
from cashu.wallet.wallet import Wallet
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import is_regtest
@pytest_asyncio.fixture(scope="function")
@@ -22,6 +23,7 @@ async def wallet(mint):
yield wallet
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_invoice(wallet: Wallet):
with TestClient(app) as client:
@@ -40,6 +42,7 @@ async def test_invoice(wallet: Wallet):
print("paid")
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_balance():
with TestClient(app) as client:
@@ -50,6 +53,7 @@ async def test_balance():
assert response.json()["mints"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_send(wallet: Wallet):
with TestClient(app) as client:
@@ -58,6 +62,7 @@ async def test_send(wallet: Wallet):
assert response.json()["balance"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_send_without_split(wallet: Wallet):
with TestClient(app) as client:
@@ -66,6 +71,7 @@ async def test_send_without_split(wallet: Wallet):
assert response.json()["balance"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_send_without_split_but_wrong_amount(wallet: Wallet):
with TestClient(app) as client:
@@ -73,6 +79,7 @@ async def test_send_without_split_but_wrong_amount(wallet: Wallet):
assert response.status_code == 400
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_pending():
with TestClient(app) as client:
@@ -80,6 +87,7 @@ async def test_pending():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_receive_all(wallet: Wallet):
with TestClient(app) as client:
@@ -89,6 +97,7 @@ async def test_receive_all(wallet: Wallet):
assert response.json()["balance"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_burn_all(wallet: Wallet):
with TestClient(app) as client:
@@ -99,6 +108,7 @@ async def test_burn_all(wallet: Wallet):
assert response.json()["balance"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_pay():
with TestClient(app) as client:
@@ -113,6 +123,7 @@ async def test_pay():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_lock():
with TestClient(app) as client:
@@ -120,6 +131,7 @@ async def test_lock():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_locks():
with TestClient(app) as client:
@@ -127,6 +139,7 @@ async def test_locks():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_invoices():
with TestClient(app) as client:
@@ -134,6 +147,7 @@ async def test_invoices():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_wallets():
with TestClient(app) as client:
@@ -141,6 +155,7 @@ async def test_wallets():
assert response.status_code == 200
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_info():
with TestClient(app) as client:
@@ -149,6 +164,7 @@ async def test_info():
assert response.json()["version"]
@pytest.mark.skipif(is_regtest, reason="regtest")
@pytest.mark.asyncio
async def test_flow(wallet: Wallet):
with TestClient(app) as client:

View File

@@ -15,6 +15,7 @@ from cashu.wallet.wallet import Wallet
from cashu.wallet.wallet import Wallet as Wallet1
from cashu.wallet.wallet import Wallet as Wallet2
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import pay_if_regtest
async def assert_err(f, msg):
@@ -59,6 +60,7 @@ async def wallet2(mint):
@pytest.mark.asyncio
async def test_create_htlc_secret(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -69,6 +71,7 @@ async def test_create_htlc_secret(wallet1: Wallet):
@pytest.mark.asyncio
async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -81,6 +84,7 @@ async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -94,6 +98,7 @@ async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -111,6 +116,7 @@ async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet)
@pytest.mark.asyncio
async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
@@ -130,6 +136,7 @@ async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
@@ -153,6 +160,7 @@ async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet
@pytest.mark.asyncio
async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
@@ -174,6 +182,7 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
wallet1: Wallet, wallet2: Wallet
):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
@@ -207,6 +216,7 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature(
wallet1: Wallet, wallet2: Wallet
):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()

View File

@@ -16,6 +16,7 @@ from cashu.wallet.wallet import Wallet
from cashu.wallet.wallet import Wallet as Wallet1
from cashu.wallet.wallet import Wallet as Wallet2
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import pay_if_regtest
async def assert_err(f, msg):
@@ -60,6 +61,7 @@ async def wallet2(mint):
@pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True)
@@ -68,6 +70,7 @@ async def test_create_p2pk_pubkey(wallet1: Wallet):
@pytest.mark.asyncio
async def test_p2pk(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test
@@ -81,6 +84,7 @@ async def test_p2pk(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2pk_sig_all(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test
@@ -96,6 +100,7 @@ async def test_p2pk_sig_all(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2pk_receive_with_wrong_private_key(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
@@ -116,6 +121,7 @@ async def test_p2pk_short_locktime_receive_with_wrong_private_key(
wallet1: Wallet, wallet2: Wallet
):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
@@ -141,6 +147,7 @@ async def test_p2pk_short_locktime_receive_with_wrong_private_key(
@pytest.mark.asyncio
async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
@@ -169,6 +176,7 @@ async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet
@pytest.mark.asyncio
async def test_p2pk_locktime_with_wrong_refund_pubkey(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await wallet2.create_p2pk_pubkey() # receiver side
# sender side
@@ -204,6 +212,7 @@ async def test_p2pk_locktime_with_second_refund_pubkey(
wallet1: Wallet, wallet2: Wallet
):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() # receiver side
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
@@ -235,6 +244,7 @@ async def test_p2pk_locktime_with_second_refund_pubkey(
@pytest.mark.asyncio
async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
@@ -256,6 +266,7 @@ async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
@@ -279,6 +290,7 @@ async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Walle
@pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
@@ -299,6 +311,7 @@ async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wal
@pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
@@ -323,6 +336,7 @@ async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wal
@pytest.mark.asyncio
async def test_p2pk_multisig_with_duplicate_publickey(wallet1: Wallet, wallet2: Wallet):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test
@@ -340,6 +354,7 @@ async def test_p2pk_multisig_with_wrong_first_private_key(
wallet1: Wallet, wallet2: Wallet
):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()

View File

@@ -12,6 +12,7 @@ from cashu.wallet.wallet import Wallet
from cashu.wallet.wallet import Wallet as Wallet1
from cashu.wallet.wallet import Wallet as Wallet2
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import pay_if_regtest
async def assert_err(f, msg: Union[str, CashuError]):
@@ -147,6 +148,7 @@ async def test_generate_secrets_from_to(wallet3: Wallet):
async def test_restore_wallet_after_mint(wallet3: Wallet):
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(64, id=invoice.id)
assert wallet3.balance == 64
await reset_wallet_db(wallet3)
@@ -177,6 +179,7 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet):
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(64, id=invoice.id)
assert wallet3.balance == 64
@@ -199,6 +202,7 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W
)
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(64, id=invoice.id)
assert wallet3.balance == 64
@@ -239,6 +243,7 @@ async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet):
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(64, id=invoice.id)
assert wallet3.balance == 64
@@ -265,6 +270,7 @@ async def test_restore_wallet_after_send_twice(
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(2)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(2, id=invoice.id)
box.add(wallet3.proofs)
assert wallet3.balance == 2
@@ -319,6 +325,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet3.mint(64, id=invoice.id)
box.add(wallet3.proofs)
assert wallet3.balance == 64