Files
nutshell/tests/test_mint_regtest.py
callebtc 92627399a5 Wallet: store quotes (#657)
* wallet_quotes_wip

* fix quote in db

* fix subscription test

* clean up api

* fix api tests

* fix balance check
2024-11-01 13:27:27 +01:00

324 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import bolt11
import pytest
import pytest_asyncio
from cashu.core.base import Amount, MeltQuote, MeltQuoteState, Method, Unit
from cashu.core.models import PostMeltQuoteRequest
from cashu.mint.ledger import Ledger
from cashu.wallet.wallet import Wallet
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import (
SLEEP_TIME,
cancel_invoice,
get_hold_invoice,
get_real_invoice,
get_real_invoice_cln,
is_fake,
pay_if_regtest,
pay_real_invoice,
settle_invoice,
)
@pytest_asyncio.fixture(scope="function")
async def wallet():
wallet = await Wallet.with_db(
url=SERVER_ENDPOINT,
db="test_data/wallet",
name="wallet",
)
await wallet.load_mint()
yield wallet
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_create_invoice(ledger: Ledger):
invoice = await ledger.backends[Method.bolt11][Unit.sat].create_invoice(
Amount(Unit.sat, 1000)
)
assert invoice.ok
assert invoice.payment_request
assert invoice.checking_id
# TEST 2: check the invoice status
status = await ledger.backends[Method.bolt11][Unit.sat].get_invoice_status(
invoice.checking_id
)
assert status.pending
# settle the invoice
await pay_if_regtest(invoice.payment_request)
# TEST 3: check the invoice status
status = await ledger.backends[Method.bolt11][Unit.sat].get_invoice_status(
invoice.checking_id
)
assert status.settled
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_get_payment_quote(ledger: Ledger):
invoice_dict = get_real_invoice(64)
request = invoice_dict["payment_request"]
payment_quote = await ledger.backends[Method.bolt11][Unit.sat].get_payment_quote(
PostMeltQuoteRequest(request=request, unit=Unit.sat.name)
)
assert payment_quote.amount == Amount(Unit.sat, 64)
assert payment_quote.checking_id
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_pay_invoice(ledger: Ledger):
invoice_dict = get_real_invoice(64)
request = invoice_dict["payment_request"]
quote = MeltQuote(
quote="test",
method=Method.bolt11.name,
unit=Unit.sat.name,
state=MeltQuoteState.unpaid,
request=request,
checking_id="test",
amount=64,
fee_reserve=0,
)
payment = await ledger.backends[Method.bolt11][Unit.sat].pay_invoice(quote, 1000)
assert payment.settled
assert payment.preimage
assert payment.checking_id
assert not payment.error_message
# TEST 2: check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
payment.checking_id
)
assert status.settled
assert status.preimage
assert not status.error_message
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_pay_invoice_failure(ledger: Ledger):
# create an invoice with the external CLN node and pay it with the external LND so that our mint backend can't pay it
request = get_real_invoice_cln(64)
# pay the invoice so that the attempt later fails
pay_real_invoice(request)
# we call get_payment_quote to get a checking_id that we will use to check for the failed pending state later with get_payment_status
payment_quote = await ledger.backends[Method.bolt11][Unit.sat].get_payment_quote(
PostMeltQuoteRequest(request=request, unit=Unit.sat.name)
)
checking_id = payment_quote.checking_id
# TEST 1: check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
checking_id
)
assert status.unknown
# TEST 2: pay the invoice
quote = MeltQuote(
quote="test",
method=Method.bolt11.name,
unit=Unit.sat.name,
state=MeltQuoteState.unpaid,
request=request,
checking_id="test",
amount=64,
fee_reserve=0,
)
payment = await ledger.backends[Method.bolt11][Unit.sat].pay_invoice(quote, 1000)
assert payment.failed
assert not payment.preimage
assert payment.error_message
assert not payment.checking_id
# TEST 3: check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
checking_id
)
assert status.failed or status.unknown
assert not status.preimage
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_pay_invoice_pending_success(ledger: Ledger):
# create a hold invoice
preimage, invoice_dict = get_hold_invoice(64)
request = str(invoice_dict["payment_request"])
# we call get_payment_quote to get a checking_id that we will use to check for the failed pending state later with get_payment_status
payment_quote = await ledger.backends[Method.bolt11][Unit.sat].get_payment_quote(
PostMeltQuoteRequest(request=request, unit=Unit.sat.name)
)
checking_id = payment_quote.checking_id
# pay the invoice
quote = MeltQuote(
quote="test",
method=Method.bolt11.name,
unit=Unit.sat.name,
state=MeltQuoteState.unpaid,
request=request,
checking_id=checking_id,
amount=64,
fee_reserve=0,
)
async def pay():
payment = await ledger.backends[Method.bolt11][Unit.sat].pay_invoice(
quote, 1000
)
return payment
task = asyncio.create_task(pay())
await asyncio.sleep(SLEEP_TIME)
# check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
quote.checking_id
)
assert status.pending
# settle the invoice
settle_invoice(preimage=preimage)
await asyncio.sleep(SLEEP_TIME)
# collect the payment
payment = await task
assert payment.settled
assert payment.preimage
assert payment.checking_id
assert not payment.error_message
# check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
quote.checking_id
)
assert status.settled
assert status.preimage
assert not status.error_message
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_lightning_pay_invoice_pending_failure(ledger: Ledger):
# create a hold invoice
preimage, invoice_dict = get_hold_invoice(64)
request = str(invoice_dict["payment_request"])
payment_hash = bolt11.decode(request).payment_hash
# we call get_payment_quote to get a checking_id that we will use to check for the failed pending state later with get_payment_status
payment_quote = await ledger.backends[Method.bolt11][Unit.sat].get_payment_quote(
PostMeltQuoteRequest(request=request, unit=Unit.sat.name)
)
checking_id = payment_quote.checking_id
# pay the invoice
quote = MeltQuote(
quote="test",
method=Method.bolt11.name,
unit=Unit.sat.name,
state=MeltQuoteState.unpaid,
request=request,
checking_id=checking_id,
amount=64,
fee_reserve=0,
)
async def pay():
payment = await ledger.backends[Method.bolt11][Unit.sat].pay_invoice(
quote, 1000
)
return payment
task = asyncio.create_task(pay())
await asyncio.sleep(SLEEP_TIME)
# check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
quote.checking_id
)
assert status.pending
# cancel the invoice
cancel_invoice(payment_hash)
await asyncio.sleep(SLEEP_TIME)
# collect the payment
payment = await task
assert payment.failed
assert not payment.preimage
# assert payment.error_message
# check the payment status
status = await ledger.backends[Method.bolt11][Unit.sat].get_payment_status(
quote.checking_id
)
assert (
status.failed or status.unknown
) # some backends send unknown instead of failed if they can't find the payment
assert not status.preimage
# assert status.error_message
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger):
# fill wallet
mint_quote = await wallet.request_mint(64)
await pay_if_regtest(mint_quote.request)
await wallet.mint(64, quote_id=mint_quote.quote)
assert wallet.balance == 64
# create hodl invoice
preimage, invoice_dict = get_hold_invoice(16)
invoice_payment_request = str(invoice_dict["payment_request"])
# wallet pays the invoice
quote = await wallet.melt_quote(invoice_payment_request)
total_amount = quote.amount + quote.fee_reserve
_, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount)
asyncio.create_task(ledger.melt(proofs=send_proofs, quote=quote.quote))
# asyncio.create_task(
# wallet.melt(
# proofs=send_proofs,
# invoice=invoice_payment_request,
# fee_reserve_sat=quote.fee_reserve,
# quote_id=quote.quote,
# )
# )
await asyncio.sleep(SLEEP_TIME)
# expect that melt quote is still pending
melt_quotes = await ledger.crud.get_all_melt_quotes_from_pending_proofs(
db=ledger.db
)
assert melt_quotes
# expect that proofs are still pending
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.pending for s in states])
# only now settle the invoice
settle_invoice(preimage=preimage)
await asyncio.sleep(SLEEP_TIME)
# expect that proofs are now spent
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.spent for s in states])
# expect that no melt quote is pending
melt_quotes = await ledger.crud.get_all_melt_quotes_from_pending_proofs(
db=ledger.db
)
assert not melt_quotes