Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mints: New melt flow #622

Merged
merged 39 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
38e7e22
`PaymentResult`
lollerfirst Sep 11, 2024
26eb30c
ledger: rely on PaymentResult instead of paid flag. Double check for …
lollerfirst Sep 11, 2024
0cba761
Merge remote-tracking branch 'origin/main' into payment-result
lollerfirst Sep 11, 2024
012178c
`None` is `PENDING`
lollerfirst Sep 12, 2024
5b818b8
make format
lollerfirst Sep 12, 2024
22e8be4
reflected changes API tests where `PaymentStatus` is used + reflected…
lollerfirst Sep 12, 2024
82ff6c5
reflect changes in blink backend and tests
lollerfirst Sep 12, 2024
0aa3c97
fix lnbits get_payment_status
lollerfirst Sep 13, 2024
12b4185
remove paid flag
callebtc Sep 19, 2024
e1f1590
fix mypy
callebtc Sep 19, 2024
2695929
remove more paid flags
callebtc Sep 19, 2024
904eb6d
fix strike mypy
callebtc Sep 19, 2024
6faa0a1
green
callebtc Sep 19, 2024
fbba4c8
shorten all state checks
callebtc Sep 19, 2024
4bf0ca2
fix
callebtc Sep 19, 2024
e3d5706
fix some tests
callebtc Sep 19, 2024
19bb7bf
gimme ✅
callebtc Sep 19, 2024
55c3fc9
fix............
callebtc Sep 19, 2024
71e680c
fix lnbits
callebtc Sep 20, 2024
ebb18d0
fix error
callebtc Sep 21, 2024
31791f8
lightning refactor
callebtc Sep 21, 2024
b9e456f
add more regtest tests
callebtc Sep 21, 2024
a6b788e
add tests for pending state and failure
callebtc Sep 21, 2024
7edb6fd
shorten checks
callebtc Sep 21, 2024
3d75d22
use match case for startup check - and remember modified checking_id …
callebtc Sep 21, 2024
2634c41
fix strike pending return
callebtc Sep 21, 2024
6f0ec64
new tests?
callebtc Sep 21, 2024
21fa2b1
refactor startup routine into get_melt_quote
callebtc Sep 21, 2024
43209dd
test with purge
callebtc Sep 21, 2024
643e1fb
refactor blink
callebtc Sep 22, 2024
5fa4717
cleanup responses
callebtc Sep 23, 2024
33f92bc
blink: return checking_id on failure
callebtc Sep 23, 2024
6f53664
fix lndgrpc try except
callebtc Sep 23, 2024
bedbc64
add more testing for melt branches
callebtc Sep 23, 2024
a43f822
speed things up a bit
callebtc Sep 23, 2024
5cbc6ce
remove comments
callebtc Sep 24, 2024
8430409
remove comments
callebtc Sep 24, 2024
08eac15
block pending melt quotes
callebtc Sep 24, 2024
805870d
remove comments
callebtc Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 56 additions & 12 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ def identifier(self) -> str:
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.PROOF_STATE

@property
def unspent(self) -> bool:
return self.state == ProofSpentState.unspent

@property
def spent(self) -> bool:
return self.state == ProofSpentState.spent

@property
def pending(self) -> bool:
return self.state == ProofSpentState.pending


class HTLCWitness(BaseModel):
preimage: Optional[str] = None
Expand Down Expand Up @@ -290,7 +302,6 @@ class MeltQuote(LedgerEvent):
unit: str
amount: int
fee_reserve: int
paid: bool
state: MeltQuoteState
created_time: Union[int, None] = None
paid_time: Union[int, None] = None
Expand Down Expand Up @@ -325,7 +336,6 @@ def from_row(cls, row: Row):
unit=row["unit"],
amount=row["amount"],
fee_reserve=row["fee_reserve"],
paid=row["paid"],
state=MeltQuoteState[row["state"]],
created_time=created_time,
paid_time=paid_time,
Expand All @@ -344,17 +354,34 @@ def identifier(self) -> str:
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.BOLT11_MELT_QUOTE

@property
def unpaid(self) -> bool:
return self.state == MeltQuoteState.unpaid

@property
def pending(self) -> bool:
return self.state == MeltQuoteState.pending

@property
def paid(self) -> bool:
return self.state == MeltQuoteState.paid

# method that is invoked when the `state` attribute is changed. to protect the state from being set to anything else if the current state is paid
def __setattr__(self, name, value):
# an unpaid quote can only be set to pending or paid
if name == "state" and self.state == MeltQuoteState.unpaid:
if name == "state" and self.unpaid:
if value not in [MeltQuoteState.pending, MeltQuoteState.paid]:
raise Exception(
f"Cannot change state of an unpaid melt quote to {value}."
)
# a paid quote can not be changed
if name == "state" and self.state == MeltQuoteState.paid:
if name == "state" and self.paid:
raise Exception("Cannot change state of a paid melt quote.")

if name == "paid":
raise Exception(
"MeltQuote does not support `paid` anymore! Use `state` instead."
)
super().__setattr__(name, value)


Expand All @@ -375,8 +402,6 @@ class MintQuote(LedgerEvent):
checking_id: str
unit: str
amount: int
paid: bool
issued: bool
state: MintQuoteState
created_time: Union[int, None] = None
paid_time: Union[int, None] = None
Expand All @@ -401,8 +426,6 @@ def from_row(cls, row: Row):
checking_id=row["checking_id"],
unit=row["unit"],
amount=row["amount"],
paid=row["paid"],
issued=row["issued"],
state=MintQuoteState[row["state"]],
created_time=created_time,
paid_time=paid_time,
Expand All @@ -417,24 +440,45 @@ def identifier(self) -> str:
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.BOLT11_MINT_QUOTE

@property
def unpaid(self) -> bool:
return self.state == MintQuoteState.unpaid

@property
def paid(self) -> bool:
return self.state == MintQuoteState.paid

@property
def pending(self) -> bool:
return self.state == MintQuoteState.pending

@property
def issued(self) -> bool:
return self.state == MintQuoteState.issued

def __setattr__(self, name, value):
# un unpaid quote can only be set to paid
if name == "state" and self.state == MintQuoteState.unpaid:
if name == "state" and self.unpaid:
if value != MintQuoteState.paid:
raise Exception(
f"Cannot change state of an unpaid mint quote to {value}."
)
# a paid quote can only be set to pending or issued
if name == "state" and self.state == MintQuoteState.paid:
if name == "state" and self.paid:
if value != MintQuoteState.pending and value != MintQuoteState.issued:
raise Exception(f"Cannot change state of a paid mint quote to {value}.")
# a pending quote can only be set to paid or issued
if name == "state" and self.state == MintQuoteState.pending:
if name == "state" and self.pending:
if value not in [MintQuoteState.paid, MintQuoteState.issued]:
raise Exception("Cannot change state of a pending mint quote.")
# an issued quote cannot be changed
if name == "state" and self.state == MintQuoteState.issued:
if name == "state" and self.issued:
raise Exception("Cannot change state of an issued mint quote.")

if name == "paid":
raise Exception(
"MintQuote does not support `paid` anymore! Use `state` instead."
)
super().__setattr__(name, value)


Expand Down
4 changes: 3 additions & 1 deletion cashu/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class PostMeltQuoteResponse(BaseModel):
fee_reserve: int # input fee reserve
paid: Optional[
bool
] # whether the request has been paid # DEPRECATED as per NUT PR #136
] = None # whether the request has been paid # DEPRECATED as per NUT PR #136
state: Optional[str] # state of the quote
expiry: Optional[int] # expiry of the quote
payment_preimage: Optional[str] = None # payment preimage
Expand All @@ -216,6 +216,8 @@ def from_melt_quote(self, melt_quote: MeltQuote) -> "PostMeltQuoteResponse":
to_dict = melt_quote.dict()
# turn state into string
to_dict["state"] = melt_quote.state.value
# add deprecated "paid" field
to_dict["paid"] = melt_quote.paid
return PostMeltQuoteResponse.parse_obj(to_dict)


Expand Down
3 changes: 2 additions & 1 deletion cashu/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ class FakeWalletSettings(MintSettings):
fakewallet_delay_outgoing_payment: Optional[float] = Field(default=3.0)
fakewallet_delay_incoming_payment: Optional[float] = Field(default=3.0)
fakewallet_stochastic_invoice: bool = Field(default=False)
fakewallet_payment_state: Optional[bool] = Field(default=None)
fakewallet_payment_state: Optional[str] = Field(default="SETTLED")
fakewallet_pay_invoice_state: Optional[str] = Field(default="SETTLED")


class MintInformation(CashuSettings):
Expand Down
64 changes: 53 additions & 11 deletions cashu/lightning/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from abc import ABC, abstractmethod
from enum import Enum, auto
from typing import AsyncGenerator, Coroutine, Optional, Union

from pydantic import BaseModel
Expand All @@ -12,8 +13,8 @@


class StatusResponse(BaseModel):
error_message: Optional[str]
balance: Union[int, float]
error_message: Optional[str] = None


class InvoiceQuoteResponse(BaseModel):
Expand All @@ -34,36 +35,77 @@ class InvoiceResponse(BaseModel):
error_message: Optional[str] = None


class PaymentResult(Enum):
SETTLED = auto()
FAILED = auto()
PENDING = auto()
UNKNOWN = auto()

def __str__(self):
return self.name


class PaymentResponse(BaseModel):
ok: Optional[bool] = None # True: paid, False: failed, None: pending or unknown
result: PaymentResult
checking_id: Optional[str] = None
fee: Optional[Amount] = None
preimage: Optional[str] = None
error_message: Optional[str] = None

@property
def pending(self) -> bool:
return self.result == PaymentResult.PENDING

@property
def settled(self) -> bool:
return self.result == PaymentResult.SETTLED

@property
def failed(self) -> bool:
return self.result == PaymentResult.FAILED

@property
def unknown(self) -> bool:
return self.result == PaymentResult.UNKNOWN


class PaymentStatus(BaseModel):
paid: Optional[bool] = None
result: PaymentResult
fee: Optional[Amount] = None
preimage: Optional[str] = None
error_message: Optional[str] = None

@property
def pending(self) -> bool:
return self.paid is not True
return self.result == PaymentResult.PENDING

@property
def settled(self) -> bool:
return self.result == PaymentResult.SETTLED

@property
def failed(self) -> bool:
return self.paid is False
return self.result == PaymentResult.FAILED

@property
def unknown(self) -> bool:
return self.result == PaymentResult.UNKNOWN

def __str__(self) -> str:
if self.paid is True:
return "settled"
elif self.paid is False:
if self.result == PaymentResult.SETTLED:
return (
"settled"
+ (f" (preimage: {self.preimage})" if self.preimage else "")
+ (f" (fee: {self.fee})" if self.fee else "")
)
elif self.result == PaymentResult.FAILED:
return "failed"
elif self.paid is None:
elif self.result == PaymentResult.PENDING:
return "still pending"
else:
return "unknown (should never happen)"
else: # self.result == PaymentResult.UNKNOWN:
return "unknown" + (
f" (Error: {self.error_message})" if self.error_message else ""
)


class LightningBackend(ABC):
Expand Down
Loading
Loading