WIP: New melt flow (#622)

* `PaymentResult`

* ledger: rely on PaymentResult instead of paid flag. Double check for payments marked pending.

* `None` is `PENDING`

* make format

* reflected changes API tests where `PaymentStatus` is used + reflected changes in lnbits

* reflect changes in blink backend and tests

* fix lnbits get_payment_status

* remove paid flag

* fix mypy

* remove more paid flags

* fix strike mypy

* green

* shorten all state checks

* fix

* fix some tests

* gimme 

* fix............

* fix lnbits

* fix error

* lightning refactor

* add more regtest tests

* add tests for pending state and failure

* shorten checks

* use match case for startup check - and remember modified checking_id from pay_invoice

* fix strike pending return

* new tests?

* refactor startup routine into get_melt_quote

* test with purge

* refactor blink

* cleanup responses

* blink: return checking_id on failure

* fix lndgrpc try except

* add more testing for melt branches

* speed things up a bit

* remove comments

* remove comments

* block pending melt quotes

* remove comments

---------

Co-authored-by: lollerfirst <lollerfirst@gmail.com>
This commit is contained in:
callebtc
2024-09-24 14:55:35 +02:00
committed by GitHub
parent 25f0763f94
commit d8d3037cc5
39 changed files with 1575 additions and 682 deletions

View File

@@ -5,10 +5,11 @@ import bolt11
import pytest
import pytest_asyncio
from cashu.core.base import MeltQuote, MeltQuoteState, Proof, ProofSpentState
from cashu.core.base import MeltQuote, MeltQuoteState, Proof
from cashu.core.crypto.aes import AESCipher
from cashu.core.db import Database
from cashu.core.settings import settings
from cashu.lightning.base import PaymentResult
from cashu.mint.crud import LedgerCrudSqlite
from cashu.mint.ledger import Ledger
from cashu.wallet.wallet import Wallet
@@ -143,8 +144,7 @@ async def create_pending_melts(
request="asdasd",
checking_id=check_id,
unit="sat",
paid=False,
state=MeltQuoteState.unpaid,
state=MeltQuoteState.pending,
amount=100,
fee_reserve=1,
)
@@ -173,8 +173,8 @@ async def test_startup_fakewallet_pending_quote_success(ledger: Ledger):
after the startup routine determines that the associated melt quote was paid."""
pending_proof, quote = await create_pending_melts(ledger)
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.pending
settings.fakewallet_payment_state = True
assert states[0].pending
settings.fakewallet_payment_state = PaymentResult.SETTLED.name
# run startup routinge
await ledger.startup_ledger()
@@ -186,7 +186,7 @@ async def test_startup_fakewallet_pending_quote_success(ledger: Ledger):
# expect that proofs are spent
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.spent
assert states[0].spent
@pytest.mark.asyncio
@@ -199,8 +199,8 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
"""
pending_proof, quote = await create_pending_melts(ledger)
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.pending
settings.fakewallet_payment_state = False
assert states[0].pending
settings.fakewallet_payment_state = PaymentResult.FAILED.name
# run startup routinge
await ledger.startup_ledger()
@@ -212,7 +212,7 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
# expect that proofs are unspent
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.unspent
assert states[0].unspent
@pytest.mark.asyncio
@@ -220,8 +220,8 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
async def test_startup_fakewallet_pending_quote_pending(ledger: Ledger):
pending_proof, quote = await create_pending_melts(ledger)
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.pending
settings.fakewallet_payment_state = None
assert states[0].pending
settings.fakewallet_payment_state = PaymentResult.PENDING.name
# run startup routinge
await ledger.startup_ledger()
@@ -233,7 +233,28 @@ async def test_startup_fakewallet_pending_quote_pending(ledger: Ledger):
# expect that proofs are still pending
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].state == ProofSpentState.pending
assert states[0].pending
@pytest.mark.asyncio
@pytest.mark.skipif(is_regtest, reason="only for fake wallet")
async def test_startup_fakewallet_pending_quote_unknown(ledger: Ledger):
pending_proof, quote = await create_pending_melts(ledger)
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].pending
settings.fakewallet_payment_state = PaymentResult.UNKNOWN.name
# run startup routinge
await ledger.startup_ledger()
# expect that melt quote is still pending
melt_quotes = await ledger.crud.get_all_melt_quotes_from_pending_proofs(
db=ledger.db
)
assert not melt_quotes
# expect that proofs are still pending
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
assert states[0].unspent
@pytest.mark.asyncio
@@ -262,7 +283,6 @@ async def test_startup_regtest_pending_quote_pending(wallet: Wallet, ledger: Led
)
)
await asyncio.sleep(SLEEP_TIME)
# settle_invoice(preimage=preimage)
# run startup routinge
await ledger.startup_ledger()
@@ -275,7 +295,7 @@ async def test_startup_regtest_pending_quote_pending(wallet: Wallet, ledger: Led
# expect that proofs are still pending
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.state == ProofSpentState.pending for s in states])
assert all([s.pending for s in states])
# only now settle the invoice
settle_invoice(preimage=preimage)
@@ -309,7 +329,7 @@ async def test_startup_regtest_pending_quote_success(wallet: Wallet, ledger: Led
await asyncio.sleep(SLEEP_TIME)
# expect that proofs are pending
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.state == ProofSpentState.pending for s in states])
assert all([s.pending for s in states])
settle_invoice(preimage=preimage)
await asyncio.sleep(SLEEP_TIME)
@@ -325,7 +345,7 @@ async def test_startup_regtest_pending_quote_success(wallet: Wallet, ledger: Led
# expect that proofs are spent
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.state == ProofSpentState.spent for s in states])
assert all([s.spent for s in states])
@pytest.mark.asyncio
@@ -360,7 +380,7 @@ async def test_startup_regtest_pending_quote_failure(wallet: Wallet, ledger: Led
# expect that proofs are pending
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.state == ProofSpentState.pending for s in states])
assert all([s.pending for s in states])
cancel_invoice(preimage_hash=preimage_hash)
await asyncio.sleep(SLEEP_TIME)
@@ -376,4 +396,72 @@ async def test_startup_regtest_pending_quote_failure(wallet: Wallet, ledger: Led
# expect that proofs are unspent
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.state == ProofSpentState.unspent for s in states])
assert all([s.unspent for s in states])
@pytest.mark.asyncio
@pytest.mark.skipif(is_fake, reason="only regtest")
async def test_startup_regtest_pending_quote_unknown(wallet: Wallet, ledger: Ledger):
"""Simulate an unknown payment by executing a pending payment, then
manipulating the melt_quote in the mint's db so that its checking_id
points to an unknown payment."""
# fill wallet
invoice = await wallet.request_mint(64)
await pay_if_regtest(invoice.bolt11)
await wallet.mint(64, id=invoice.id)
assert wallet.balance == 64
# create hodl invoice
preimage, invoice_dict = get_hold_invoice(16)
invoice_payment_request = str(invoice_dict["payment_request"])
invoice_obj = bolt11.decode(invoice_payment_request)
preimage_hash = invoice_obj.payment_hash
# 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(
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 proofs are pending
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.pending for s in states])
# before we cancel the payment, we manipulate the melt_quote's checking_id so
# that the mint fails to look up the payment and treats the payment as failed during startup
melt_quote = await ledger.crud.get_melt_quote_by_request(
db=ledger.db, request=invoice_payment_request
)
assert melt_quote
assert melt_quote.pending
# manipulate the checking_id 32 bytes hexadecmial
melt_quote.checking_id = "a" * 64
await ledger.crud.update_melt_quote(quote=melt_quote, db=ledger.db)
await asyncio.sleep(SLEEP_TIME)
# run startup routinge
await ledger.startup_ledger()
# 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
# expect that proofs are unspent
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
assert all([s.unspent for s in states])
# clean up
cancel_invoice(preimage_hash=preimage_hash)