Files
nutshell/tests/test_wallet_regtest_mpp.py
lollerfirst ca272bc20f Mint: Add clnrest.py Lightning backend (#551)
* log cln error

* return a string

* update corelightningrest to work with latest ver using rune

* fix mpp spec and backend support check

* refactor validation in ledger

* remove weird error

* fix mpp melt model

* corelightningrest.py: Added Multi-Mint payout support
lndrest.py: fix `quote.amount` is not always in sats + better checks

* small fix

* Fix quote.unit str2unit conversion + add missing imports

* settings enable mpp corelightning (default false)

* small fix

* fix `paid_invoice_stream`

* make format

* handle runes

* load rune

* rename to MINT_CORELIGHTNING_REST_RUNE

* try without cert

* port

* try except callback dispatcher

* clean up cln-rest streaming parser

* conftest: mint_corelightning_enable_mpp

* enable mpp in regtest.yaml

* fix error handling clnrest, remove lndrest changes

* CLNRest + CoreLightningRest

* clean up corelightningrest and get last index before starting the stream

* clean up

---------

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
Co-authored-by: Richard Bensberg <r@coinbatsu.com>
2024-06-30 20:36:19 +02:00

128 lines
4.5 KiB
Python

import asyncio
from typing import List
import pytest
import pytest_asyncio
from cashu.core.base import Method, Proof
from cashu.mint.ledger import Ledger
from cashu.wallet.wallet import Wallet
from tests.conftest import SERVER_ENDPOINT
from tests.helpers import (
get_real_invoice,
is_fake,
pay_if_regtest,
)
@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_regtest_pay_mpp(wallet: Wallet, ledger: Ledger):
# make sure that mpp is supported by the bolt11-sat backend
if not ledger.backends[Method["bolt11"]][wallet.unit].supports_mpp:
pytest.skip("backend does not support mpp")
# make sure wallet knows the backend supports mpp
assert wallet.mint_info.supports_mpp("bolt11", wallet.unit)
# top up wallet twice so we have enough for two payments
topup_invoice = await wallet.request_mint(128)
pay_if_regtest(topup_invoice.bolt11)
proofs1 = await wallet.mint(128, id=topup_invoice.id)
assert wallet.balance == 128
topup_invoice = await wallet.request_mint(128)
pay_if_regtest(topup_invoice.bolt11)
proofs2 = await wallet.mint(128, id=topup_invoice.id)
assert wallet.balance == 256
# this is the invoice we want to pay in two parts
invoice_dict = get_real_invoice(64)
invoice_payment_request = invoice_dict["payment_request"]
async def pay_mpp(amount: int, proofs: List[Proof], delay: float = 0.0):
await asyncio.sleep(delay)
# wallet pays 32 sat of the invoice
quote = await wallet.melt_quote(invoice_payment_request, amount=amount)
assert quote.amount == amount
await wallet.melt(
proofs,
invoice_payment_request,
fee_reserve_sat=quote.fee_reserve,
quote_id=quote.quote,
)
# call pay_mpp twice in parallel to pay the full invoice
# we delay the second payment so that the wallet doesn't derive the same blindedmessages twice due to a race condition
await asyncio.gather(pay_mpp(32, proofs1), pay_mpp(32, proofs2, delay=0.5))
assert wallet.balance <= 256 - 64
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_regtest_pay_mpp_incomplete_payment(wallet: Wallet, ledger: Ledger):
# make sure that mpp is supported by the bolt11-sat backend
if not ledger.backends[Method["bolt11"]][wallet.unit].supports_mpp:
pytest.skip("backend does not support mpp")
# make sure wallet knows the backend supports mpp
assert wallet.mint_info.supports_mpp("bolt11", wallet.unit)
# top up wallet twice so we have enough for three payments
topup_invoice = await wallet.request_mint(128)
pay_if_regtest(topup_invoice.bolt11)
proofs1 = await wallet.mint(128, id=topup_invoice.id)
assert wallet.balance == 128
topup_invoice = await wallet.request_mint(128)
pay_if_regtest(topup_invoice.bolt11)
proofs2 = await wallet.mint(128, id=topup_invoice.id)
assert wallet.balance == 256
topup_invoice = await wallet.request_mint(128)
pay_if_regtest(topup_invoice.bolt11)
proofs3 = await wallet.mint(128, id=topup_invoice.id)
assert wallet.balance == 384
# this is the invoice we want to pay in two parts
invoice_dict = get_real_invoice(64)
invoice_payment_request = invoice_dict["payment_request"]
async def pay_mpp(amount: int, proofs: List[Proof], delay: float = 0.0):
await asyncio.sleep(delay)
# wallet pays 32 sat of the invoice
quote = await wallet.melt_quote(invoice_payment_request, amount=amount)
assert quote.amount == amount
await wallet.melt(
proofs,
invoice_payment_request,
fee_reserve_sat=quote.fee_reserve,
quote_id=quote.quote,
)
# instead: call pay_mpp twice in the background, sleep for a bit, then check if the payment was successful (it should not be)
asyncio.create_task(pay_mpp(32, proofs1))
asyncio.create_task(pay_mpp(16, proofs2, delay=0.5))
await asyncio.sleep(2)
# payment is still pending because the full amount has not been paid
assert wallet.balance == 384
# send the remaining 16 sat to complete the payment
asyncio.create_task(pay_mpp(16, proofs3, delay=0.5))
await asyncio.sleep(2)
assert wallet.balance <= 384 - 64