mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 18:44:20 +01:00
NUT-04 and NUT-05: Add state field to quotes (#560)
* wip adding states, tests failing * add state field to quotes * responses from quotes * store correct state * cleaner test * fix swap check * oops
This commit is contained in:
@@ -48,7 +48,7 @@ class DLEQWallet(BaseModel):
|
|||||||
# ------- PROOFS -------
|
# ------- PROOFS -------
|
||||||
|
|
||||||
|
|
||||||
class SpentState(Enum):
|
class ProofSpentState(Enum):
|
||||||
unspent = "UNSPENT"
|
unspent = "UNSPENT"
|
||||||
spent = "SPENT"
|
spent = "SPENT"
|
||||||
pending = "PENDING"
|
pending = "PENDING"
|
||||||
@@ -59,13 +59,13 @@ class SpentState(Enum):
|
|||||||
|
|
||||||
class ProofState(LedgerEvent):
|
class ProofState(LedgerEvent):
|
||||||
Y: str
|
Y: str
|
||||||
state: SpentState
|
state: ProofSpentState
|
||||||
witness: Optional[str] = None
|
witness: Optional[str] = None
|
||||||
|
|
||||||
@root_validator()
|
@root_validator()
|
||||||
def check_witness(cls, values):
|
def check_witness(cls, values):
|
||||||
state, witness = values.get("state"), values.get("witness")
|
state, witness = values.get("state"), values.get("witness")
|
||||||
if witness is not None and state != SpentState.spent:
|
if witness is not None and state != ProofSpentState.spent:
|
||||||
raise ValueError('Witness can only be set if the spent state is "SPENT"')
|
raise ValueError('Witness can only be set if the spent state is "SPENT"')
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@@ -268,6 +268,15 @@ class Invoice(BaseModel):
|
|||||||
time_paid: Union[None, str, int, float] = ""
|
time_paid: Union[None, str, int, float] = ""
|
||||||
|
|
||||||
|
|
||||||
|
class MeltQuoteState(Enum):
|
||||||
|
unpaid = "UNPAID"
|
||||||
|
pending = "PENDING"
|
||||||
|
paid = "PAID"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class MeltQuote(LedgerEvent):
|
class MeltQuote(LedgerEvent):
|
||||||
quote: str
|
quote: str
|
||||||
method: str
|
method: str
|
||||||
@@ -277,6 +286,7 @@ class MeltQuote(LedgerEvent):
|
|||||||
amount: int
|
amount: int
|
||||||
fee_reserve: int
|
fee_reserve: int
|
||||||
paid: bool
|
paid: bool
|
||||||
|
state: MeltQuoteState
|
||||||
created_time: Union[int, None] = None
|
created_time: Union[int, None] = None
|
||||||
paid_time: Union[int, None] = None
|
paid_time: Union[int, None] = None
|
||||||
fee_paid: int = 0
|
fee_paid: int = 0
|
||||||
@@ -303,6 +313,7 @@ class MeltQuote(LedgerEvent):
|
|||||||
amount=row["amount"],
|
amount=row["amount"],
|
||||||
fee_reserve=row["fee_reserve"],
|
fee_reserve=row["fee_reserve"],
|
||||||
paid=row["paid"],
|
paid=row["paid"],
|
||||||
|
state=MeltQuoteState[row["state"]],
|
||||||
created_time=created_time,
|
created_time=created_time,
|
||||||
paid_time=paid_time,
|
paid_time=paid_time,
|
||||||
fee_paid=row["fee_paid"],
|
fee_paid=row["fee_paid"],
|
||||||
@@ -318,6 +329,27 @@ class MeltQuote(LedgerEvent):
|
|||||||
def kind(self) -> JSONRPCSubscriptionKinds:
|
def kind(self) -> JSONRPCSubscriptionKinds:
|
||||||
return JSONRPCSubscriptionKinds.BOLT11_MELT_QUOTE
|
return JSONRPCSubscriptionKinds.BOLT11_MELT_QUOTE
|
||||||
|
|
||||||
|
# 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 value != MeltQuoteState.pending and value != MeltQuoteState.paid:
|
||||||
|
raise Exception("Cannot change state of an unpaid quote.")
|
||||||
|
# a paid quote can not be changed
|
||||||
|
if name == "state" and self.state == MeltQuoteState.paid:
|
||||||
|
raise Exception("Cannot change state of a paid quote.")
|
||||||
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
|
|
||||||
|
class MintQuoteState(Enum):
|
||||||
|
unpaid = "UNPAID"
|
||||||
|
paid = "PAID"
|
||||||
|
pending = "PENDING"
|
||||||
|
issued = "ISSUED"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class MintQuote(LedgerEvent):
|
class MintQuote(LedgerEvent):
|
||||||
quote: str
|
quote: str
|
||||||
@@ -328,6 +360,7 @@ class MintQuote(LedgerEvent):
|
|||||||
amount: int
|
amount: int
|
||||||
paid: bool
|
paid: bool
|
||||||
issued: bool
|
issued: bool
|
||||||
|
state: MintQuoteState
|
||||||
created_time: Union[int, None] = None
|
created_time: Union[int, None] = None
|
||||||
paid_time: Union[int, None] = None
|
paid_time: Union[int, None] = None
|
||||||
expiry: Optional[int] = None
|
expiry: Optional[int] = None
|
||||||
@@ -353,6 +386,7 @@ class MintQuote(LedgerEvent):
|
|||||||
amount=row["amount"],
|
amount=row["amount"],
|
||||||
paid=row["paid"],
|
paid=row["paid"],
|
||||||
issued=row["issued"],
|
issued=row["issued"],
|
||||||
|
state=MintQuoteState[row["state"]],
|
||||||
created_time=created_time,
|
created_time=created_time,
|
||||||
paid_time=paid_time,
|
paid_time=paid_time,
|
||||||
)
|
)
|
||||||
@@ -366,6 +400,24 @@ class MintQuote(LedgerEvent):
|
|||||||
def kind(self) -> JSONRPCSubscriptionKinds:
|
def kind(self) -> JSONRPCSubscriptionKinds:
|
||||||
return JSONRPCSubscriptionKinds.BOLT11_MINT_QUOTE
|
return JSONRPCSubscriptionKinds.BOLT11_MINT_QUOTE
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
# un unpaid quote can only be set to paid
|
||||||
|
if name == "state" and self.state == MintQuoteState.unpaid:
|
||||||
|
if value != MintQuoteState.paid:
|
||||||
|
raise Exception("Cannot change state of an unpaid quote.")
|
||||||
|
# a paid quote can only be set to pending or issued
|
||||||
|
if name == "state" and self.state == MintQuoteState.paid:
|
||||||
|
if value != MintQuoteState.pending and value != MintQuoteState.issued:
|
||||||
|
raise Exception(f"Cannot change state of a paid quote to {value}.")
|
||||||
|
# a pending quote can only be set to paid or issued
|
||||||
|
if name == "state" and self.state == MintQuoteState.pending:
|
||||||
|
if value not in [MintQuoteState.paid, MintQuoteState.issued]:
|
||||||
|
raise Exception("Cannot change state of a pending quote.")
|
||||||
|
# an issued quote cannot be changed
|
||||||
|
if name == "state" and self.state == MintQuoteState.issued:
|
||||||
|
raise Exception("Cannot change state of an issued quote.")
|
||||||
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
|
|
||||||
# ------- KEYSETS -------
|
# ------- KEYSETS -------
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from .base import (
|
|||||||
BlindedMessage,
|
BlindedMessage,
|
||||||
BlindedMessage_Deprecated,
|
BlindedMessage_Deprecated,
|
||||||
BlindedSignature,
|
BlindedSignature,
|
||||||
|
MeltQuote,
|
||||||
|
MintQuote,
|
||||||
Proof,
|
Proof,
|
||||||
ProofState,
|
ProofState,
|
||||||
)
|
)
|
||||||
@@ -98,9 +100,19 @@ class PostMintQuoteRequest(BaseModel):
|
|||||||
class PostMintQuoteResponse(BaseModel):
|
class PostMintQuoteResponse(BaseModel):
|
||||||
quote: str # quote id
|
quote: str # quote id
|
||||||
request: str # input payment request
|
request: str # input payment request
|
||||||
paid: bool # whether the request has been paid
|
paid: Optional[
|
||||||
|
bool
|
||||||
|
] # whether the request has been paid # DEPRECATED as per NUT PR #141
|
||||||
|
state: str # state of the quote
|
||||||
expiry: Optional[int] # expiry of the quote
|
expiry: Optional[int] # expiry of the quote
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mint_quote(self, mint_quote: MintQuote) -> "PostMintQuoteResponse":
|
||||||
|
to_dict = mint_quote.dict()
|
||||||
|
# turn state into string
|
||||||
|
to_dict["state"] = mint_quote.state.value
|
||||||
|
return PostMintQuoteResponse.parse_obj(to_dict)
|
||||||
|
|
||||||
|
|
||||||
# ------- API: MINT -------
|
# ------- API: MINT -------
|
||||||
|
|
||||||
@@ -168,9 +180,17 @@ class PostMeltQuoteResponse(BaseModel):
|
|||||||
quote: str # quote id
|
quote: str # quote id
|
||||||
amount: int # input amount
|
amount: int # input amount
|
||||||
fee_reserve: int # input fee reserve
|
fee_reserve: int # input fee reserve
|
||||||
paid: bool # whether the request has been paid
|
paid: bool # whether the request has been paid # DEPRECATED as per NUT PR #136
|
||||||
|
state: str # state of the quote
|
||||||
expiry: Optional[int] # expiry of the quote
|
expiry: Optional[int] # expiry of the quote
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
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
|
||||||
|
return PostMeltQuoteResponse.parse_obj(to_dict)
|
||||||
|
|
||||||
|
|
||||||
# ------- API: MELT -------
|
# ------- API: MELT -------
|
||||||
|
|
||||||
|
|||||||
@@ -433,8 +433,8 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
f"""
|
f"""
|
||||||
INSERT INTO {table_with_schema(db, 'mint_quotes')}
|
INSERT INTO {table_with_schema(db, 'mint_quotes')}
|
||||||
(quote, method, request, checking_id, unit, amount, issued, paid, created_time, paid_time)
|
(quote, method, request, checking_id, unit, amount, issued, paid, state, created_time, paid_time)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
quote.quote,
|
quote.quote,
|
||||||
@@ -445,6 +445,7 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
quote.amount,
|
quote.amount,
|
||||||
quote.issued,
|
quote.issued,
|
||||||
quote.paid,
|
quote.paid,
|
||||||
|
quote.state.name,
|
||||||
timestamp_from_seconds(db, quote.created_time),
|
timestamp_from_seconds(db, quote.created_time),
|
||||||
timestamp_from_seconds(db, quote.paid_time),
|
timestamp_from_seconds(db, quote.paid_time),
|
||||||
),
|
),
|
||||||
@@ -510,10 +511,11 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
) -> None:
|
) -> None:
|
||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
f"UPDATE {table_with_schema(db, 'mint_quotes')} SET issued = ?, paid = ?,"
|
f"UPDATE {table_with_schema(db, 'mint_quotes')} SET issued = ?, paid = ?,"
|
||||||
" paid_time = ? WHERE quote = ?",
|
" state = ?, paid_time = ? WHERE quote = ?",
|
||||||
(
|
(
|
||||||
quote.issued,
|
quote.issued,
|
||||||
quote.paid,
|
quote.paid,
|
||||||
|
quote.state.name,
|
||||||
timestamp_from_seconds(db, quote.paid_time),
|
timestamp_from_seconds(db, quote.paid_time),
|
||||||
quote.quote,
|
quote.quote,
|
||||||
),
|
),
|
||||||
@@ -546,8 +548,8 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
f"""
|
f"""
|
||||||
INSERT INTO {table_with_schema(db, 'melt_quotes')}
|
INSERT INTO {table_with_schema(db, 'melt_quotes')}
|
||||||
(quote, method, request, checking_id, unit, amount, fee_reserve, paid, created_time, paid_time, fee_paid, proof)
|
(quote, method, request, checking_id, unit, amount, fee_reserve, paid, state, created_time, paid_time, fee_paid, proof)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
quote.quote,
|
quote.quote,
|
||||||
@@ -558,6 +560,7 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
quote.amount,
|
quote.amount,
|
||||||
quote.fee_reserve or 0,
|
quote.fee_reserve or 0,
|
||||||
quote.paid,
|
quote.paid,
|
||||||
|
quote.state.name,
|
||||||
timestamp_from_seconds(db, quote.created_time),
|
timestamp_from_seconds(db, quote.created_time),
|
||||||
timestamp_from_seconds(db, quote.paid_time),
|
timestamp_from_seconds(db, quote.paid_time),
|
||||||
quote.fee_paid,
|
quote.fee_paid,
|
||||||
@@ -608,10 +611,11 @@ class LedgerCrudSqlite(LedgerCrud):
|
|||||||
conn: Optional[Connection] = None,
|
conn: Optional[Connection] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
f"UPDATE {table_with_schema(db, 'melt_quotes')} SET paid = ?, fee_paid = ?,"
|
f"UPDATE {table_with_schema(db, 'melt_quotes')} SET paid = ?, state = ?,"
|
||||||
" paid_time = ?, proof = ? WHERE quote = ?",
|
" fee_paid = ?, paid_time = ?, proof = ? WHERE quote = ?",
|
||||||
(
|
(
|
||||||
quote.paid,
|
quote.paid,
|
||||||
|
quote.state.name,
|
||||||
quote.fee_paid,
|
quote.fee_paid,
|
||||||
timestamp_from_seconds(db, quote.paid_time),
|
timestamp_from_seconds(db, quote.paid_time),
|
||||||
quote.proof,
|
quote.proof,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from ...core.base import Proof, ProofState, SpentState
|
from ...core.base import Proof, ProofSpentState, ProofState
|
||||||
from ...core.db import Database
|
from ...core.db import Database
|
||||||
from ..crud import LedgerCrud
|
from ..crud import LedgerCrud
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@ class DbReadHelper:
|
|||||||
proofs_pending = await self._get_proofs_pending(Ys)
|
proofs_pending = await self._get_proofs_pending(Ys)
|
||||||
for Y in Ys:
|
for Y in Ys:
|
||||||
if Y not in proofs_spent and Y not in proofs_pending:
|
if Y not in proofs_spent and Y not in proofs_pending:
|
||||||
states.append(ProofState(Y=Y, state=SpentState.unspent))
|
states.append(ProofState(Y=Y, state=ProofSpentState.unspent))
|
||||||
elif Y not in proofs_spent and Y in proofs_pending:
|
elif Y not in proofs_spent and Y in proofs_pending:
|
||||||
states.append(ProofState(Y=Y, state=SpentState.pending))
|
states.append(ProofState(Y=Y, state=ProofSpentState.pending))
|
||||||
else:
|
else:
|
||||||
states.append(
|
states.append(
|
||||||
ProofState(
|
ProofState(
|
||||||
Y=Y,
|
Y=Y,
|
||||||
state=SpentState.spent,
|
state=ProofSpentState.spent,
|
||||||
witness=proofs_spent[Y].witness,
|
witness=proofs_spent[Y].witness,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from ...core.base import Proof, ProofState, SpentState
|
from ...core.base import Proof, ProofSpentState, ProofState
|
||||||
from ...core.db import Connection, Database, get_db_connection
|
from ...core.db import Connection, Database, get_db_connection
|
||||||
from ...core.errors import (
|
from ...core.errors import (
|
||||||
TransactionError,
|
TransactionError,
|
||||||
@@ -50,7 +50,7 @@ class DbWriteHelper:
|
|||||||
proof=p, db=self.db, quote_id=quote_id, conn=conn
|
proof=p, db=self.db, quote_id=quote_id, conn=conn
|
||||||
)
|
)
|
||||||
await self.events.submit(
|
await self.events.submit(
|
||||||
ProofState(Y=p.Y, state=SpentState.pending)
|
ProofState(Y=p.Y, state=ProofSpentState.pending)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to set proofs pending: {e}")
|
logger.error(f"Failed to set proofs pending: {e}")
|
||||||
@@ -72,7 +72,7 @@ class DbWriteHelper:
|
|||||||
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)
|
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)
|
||||||
if not spent:
|
if not spent:
|
||||||
await self.events.submit(
|
await self.events.submit(
|
||||||
ProofState(Y=p.Y, state=SpentState.unspent)
|
ProofState(Y=p.Y, state=ProofSpentState.unspent)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _validate_proofs_pending(
|
async def _validate_proofs_pending(
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ class LedgerEventManager:
|
|||||||
|
|
||||||
def serialize_event(self, event: LedgerEvent) -> dict:
|
def serialize_event(self, event: LedgerEvent) -> dict:
|
||||||
if isinstance(event, MintQuote):
|
if isinstance(event, MintQuote):
|
||||||
return_dict = PostMintQuoteResponse.parse_obj(event.dict()).dict()
|
return_dict = PostMintQuoteResponse.from_mint_quote(event).dict()
|
||||||
elif isinstance(event, MeltQuote):
|
elif isinstance(event, MeltQuote):
|
||||||
return_dict = PostMeltQuoteResponse.parse_obj(event.dict()).dict()
|
return_dict = PostMeltQuoteResponse.from_melt_quote(event).dict()
|
||||||
elif isinstance(event, ProofState):
|
elif isinstance(event, ProofState):
|
||||||
return_dict = event.dict(exclude_unset=True, exclude_none=True)
|
return_dict = event.dict(exclude_unset=True, exclude_none=True)
|
||||||
return return_dict
|
return return_dict
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ from ..core.base import (
|
|||||||
BlindedMessage,
|
BlindedMessage,
|
||||||
BlindedSignature,
|
BlindedSignature,
|
||||||
MeltQuote,
|
MeltQuote,
|
||||||
|
MeltQuoteState,
|
||||||
Method,
|
Method,
|
||||||
MintKeyset,
|
MintKeyset,
|
||||||
MintQuote,
|
MintQuote,
|
||||||
|
MintQuoteState,
|
||||||
Proof,
|
Proof,
|
||||||
|
ProofSpentState,
|
||||||
ProofState,
|
ProofState,
|
||||||
SpentState,
|
|
||||||
Unit,
|
Unit,
|
||||||
)
|
)
|
||||||
from ..core.crypto import b_dhke
|
from ..core.crypto import b_dhke
|
||||||
@@ -153,6 +155,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
logger.info(f"Melt quote {quote.quote} state: paid")
|
logger.info(f"Melt quote {quote.quote} state: paid")
|
||||||
quote.paid_time = int(time.time())
|
quote.paid_time = int(time.time())
|
||||||
quote.paid = True
|
quote.paid = True
|
||||||
|
quote.state = MeltQuoteState.paid
|
||||||
if payment.fee:
|
if payment.fee:
|
||||||
quote.fee_paid = payment.fee.to(Unit[quote.unit]).amount
|
quote.fee_paid = payment.fee.to(Unit[quote.unit]).amount
|
||||||
quote.proof = payment.preimage or ""
|
quote.proof = payment.preimage or ""
|
||||||
@@ -302,7 +305,9 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
proof=p, db=self.db, quote_id=quote_id, conn=conn
|
proof=p, db=self.db, quote_id=quote_id, conn=conn
|
||||||
)
|
)
|
||||||
await self.events.submit(
|
await self.events.submit(
|
||||||
ProofState(Y=p.Y, state=SpentState.spent, witness=p.witness or None)
|
ProofState(
|
||||||
|
Y=p.Y, state=ProofSpentState.spent, witness=p.witness or None
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _generate_change_promises(
|
async def _generate_change_promises(
|
||||||
@@ -429,6 +434,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
amount=quote_request.amount,
|
amount=quote_request.amount,
|
||||||
issued=False,
|
issued=False,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MintQuoteState.unpaid,
|
||||||
created_time=int(time.time()),
|
created_time=int(time.time()),
|
||||||
expiry=expiry,
|
expiry=expiry,
|
||||||
)
|
)
|
||||||
@@ -455,7 +461,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
|
|
||||||
unit, method = self._verify_and_get_unit_method(quote.unit, quote.method)
|
unit, method = self._verify_and_get_unit_method(quote.unit, quote.method)
|
||||||
|
|
||||||
if not quote.paid:
|
if quote.state == MintQuoteState.unpaid:
|
||||||
if not quote.checking_id:
|
if not quote.checking_id:
|
||||||
raise CashuError("quote has no checking id")
|
raise CashuError("quote has no checking id")
|
||||||
logger.trace(f"Lightning: checking invoice {quote.checking_id}")
|
logger.trace(f"Lightning: checking invoice {quote.checking_id}")
|
||||||
@@ -465,6 +471,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
if status.paid:
|
if status.paid:
|
||||||
logger.trace(f"Setting quote {quote_id} as paid")
|
logger.trace(f"Setting quote {quote_id} as paid")
|
||||||
quote.paid = True
|
quote.paid = True
|
||||||
|
quote.state = MintQuoteState.paid
|
||||||
quote.paid_time = int(time.time())
|
quote.paid_time = int(time.time())
|
||||||
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
||||||
await self.events.submit(quote)
|
await self.events.submit(quote)
|
||||||
@@ -509,6 +516,12 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
raise QuoteNotPaidError()
|
raise QuoteNotPaidError()
|
||||||
if quote.issued:
|
if quote.issued:
|
||||||
raise TransactionError("quote already issued")
|
raise TransactionError("quote already issued")
|
||||||
|
|
||||||
|
if not quote.state == MintQuoteState.paid:
|
||||||
|
raise QuoteNotPaidError()
|
||||||
|
if quote.state == MintQuoteState.issued:
|
||||||
|
raise TransactionError("quote already issued")
|
||||||
|
|
||||||
if not quote.unit == output_unit.name:
|
if not quote.unit == output_unit.name:
|
||||||
raise TransactionError("quote unit does not match output unit")
|
raise TransactionError("quote unit does not match output unit")
|
||||||
if not quote.amount == sum_amount_outputs:
|
if not quote.amount == sum_amount_outputs:
|
||||||
@@ -518,6 +531,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
|
|
||||||
logger.trace(f"crud: setting quote {quote_id} as issued")
|
logger.trace(f"crud: setting quote {quote_id} as issued")
|
||||||
quote.issued = True
|
quote.issued = True
|
||||||
|
quote.state = MintQuoteState.issued
|
||||||
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
||||||
|
|
||||||
promises = await self._generate_promises(outputs)
|
promises = await self._generate_promises(outputs)
|
||||||
@@ -549,6 +563,12 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
raise TransactionError("mint quote already paid")
|
raise TransactionError("mint quote already paid")
|
||||||
if mint_quote.issued:
|
if mint_quote.issued:
|
||||||
raise TransactionError("mint quote already issued")
|
raise TransactionError("mint quote already issued")
|
||||||
|
|
||||||
|
if mint_quote.state == MintQuoteState.issued:
|
||||||
|
raise TransactionError("mint quote already issued")
|
||||||
|
if mint_quote.state != MintQuoteState.unpaid:
|
||||||
|
raise TransactionError("mint quote already paid")
|
||||||
|
|
||||||
if not mint_quote.checking_id:
|
if not mint_quote.checking_id:
|
||||||
raise TransactionError("mint quote has no checking id")
|
raise TransactionError("mint quote has no checking id")
|
||||||
if melt_quote.is_mpp:
|
if melt_quote.is_mpp:
|
||||||
@@ -658,6 +678,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
unit=unit.name,
|
unit=unit.name,
|
||||||
amount=payment_quote.amount.to(unit).amount,
|
amount=payment_quote.amount.to(unit).amount,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MeltQuoteState.unpaid,
|
||||||
fee_reserve=payment_quote.fee.to(unit).amount,
|
fee_reserve=payment_quote.fee.to(unit).amount,
|
||||||
created_time=int(time.time()),
|
created_time=int(time.time()),
|
||||||
expiry=expiry,
|
expiry=expiry,
|
||||||
@@ -670,6 +691,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
amount=quote.amount,
|
amount=quote.amount,
|
||||||
fee_reserve=quote.fee_reserve,
|
fee_reserve=quote.fee_reserve,
|
||||||
paid=quote.paid,
|
paid=quote.paid,
|
||||||
|
state=quote.state.value,
|
||||||
expiry=quote.expiry,
|
expiry=quote.expiry,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -714,6 +736,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
if status.paid:
|
if status.paid:
|
||||||
logger.trace(f"Setting quote {quote_id} as paid")
|
logger.trace(f"Setting quote {quote_id} as paid")
|
||||||
melt_quote.paid = True
|
melt_quote.paid = True
|
||||||
|
melt_quote.state = MeltQuoteState.paid
|
||||||
if status.fee:
|
if status.fee:
|
||||||
melt_quote.fee_paid = status.fee.to(unit).amount
|
melt_quote.fee_paid = status.fee.to(unit).amount
|
||||||
if status.preimage:
|
if status.preimage:
|
||||||
@@ -753,6 +776,8 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
# we settle the transaction internally
|
# we settle the transaction internally
|
||||||
if melt_quote.paid:
|
if melt_quote.paid:
|
||||||
raise TransactionError("melt quote already paid")
|
raise TransactionError("melt quote already paid")
|
||||||
|
if melt_quote.state != MeltQuoteState.unpaid:
|
||||||
|
raise TransactionError("melt quote already paid")
|
||||||
|
|
||||||
# verify amounts from bolt11 invoice
|
# verify amounts from bolt11 invoice
|
||||||
bolt11_request = melt_quote.request
|
bolt11_request = melt_quote.request
|
||||||
@@ -768,11 +793,15 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
raise TransactionError("units do not match")
|
raise TransactionError("units do not match")
|
||||||
if not mint_quote.method == melt_quote.method:
|
if not mint_quote.method == melt_quote.method:
|
||||||
raise TransactionError("methods do not match")
|
raise TransactionError("methods do not match")
|
||||||
|
|
||||||
if mint_quote.paid:
|
if mint_quote.paid:
|
||||||
raise TransactionError("mint quote already paid")
|
raise TransactionError("mint quote already paid")
|
||||||
if mint_quote.issued:
|
if mint_quote.issued:
|
||||||
raise TransactionError("mint quote already issued")
|
raise TransactionError("mint quote already issued")
|
||||||
|
|
||||||
|
if mint_quote.state != MintQuoteState.unpaid:
|
||||||
|
raise TransactionError("mint quote already paid")
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Settling bolt11 payment internally: {melt_quote.quote} ->"
|
f"Settling bolt11 payment internally: {melt_quote.quote} ->"
|
||||||
f" {mint_quote.quote} ({melt_quote.amount} {melt_quote.unit})"
|
f" {mint_quote.quote} ({melt_quote.amount} {melt_quote.unit})"
|
||||||
@@ -780,9 +809,11 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
|
|
||||||
melt_quote.fee_paid = 0 # no internal fees
|
melt_quote.fee_paid = 0 # no internal fees
|
||||||
melt_quote.paid = True
|
melt_quote.paid = True
|
||||||
|
melt_quote.state = MeltQuoteState.paid
|
||||||
melt_quote.paid_time = int(time.time())
|
melt_quote.paid_time = int(time.time())
|
||||||
|
|
||||||
mint_quote.paid = True
|
mint_quote.paid = True
|
||||||
|
mint_quote.state = MintQuoteState.paid
|
||||||
mint_quote.paid_time = melt_quote.paid_time
|
mint_quote.paid_time = melt_quote.paid_time
|
||||||
|
|
||||||
async with get_db_connection(self.db) as conn:
|
async with get_db_connection(self.db) as conn:
|
||||||
@@ -821,7 +852,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
melt_quote.unit, melt_quote.method
|
melt_quote.unit, melt_quote.method
|
||||||
)
|
)
|
||||||
|
|
||||||
if melt_quote.paid:
|
if melt_quote.state != MeltQuoteState.unpaid:
|
||||||
raise TransactionError("melt quote already paid")
|
raise TransactionError("melt quote already paid")
|
||||||
|
|
||||||
# make sure that the outputs (for fee return) are in the same unit as the quote
|
# make sure that the outputs (for fee return) are in the same unit as the quote
|
||||||
@@ -866,7 +897,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
# settle the transaction internally if there is a mint quote with the same payment request
|
# settle the transaction internally if there is a mint quote with the same payment request
|
||||||
melt_quote = await self.melt_mint_settle_internally(melt_quote, proofs)
|
melt_quote = await self.melt_mint_settle_internally(melt_quote, proofs)
|
||||||
# quote not paid yet (not internal), pay it with the backend
|
# quote not paid yet (not internal), pay it with the backend
|
||||||
if not melt_quote.paid:
|
if not melt_quote.paid and melt_quote.state == MeltQuoteState.unpaid:
|
||||||
logger.debug(f"Lightning: pay invoice {melt_quote.request}")
|
logger.debug(f"Lightning: pay invoice {melt_quote.request}")
|
||||||
payment = await self.backends[method][unit].pay_invoice(
|
payment = await self.backends[method][unit].pay_invoice(
|
||||||
melt_quote, melt_quote.fee_reserve * 1000
|
melt_quote, melt_quote.fee_reserve * 1000
|
||||||
@@ -887,6 +918,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
melt_quote.proof = payment.preimage
|
melt_quote.proof = payment.preimage
|
||||||
# set quote as paid
|
# set quote as paid
|
||||||
melt_quote.paid = True
|
melt_quote.paid = True
|
||||||
|
melt_quote.state = MeltQuoteState.paid
|
||||||
melt_quote.paid_time = int(time.time())
|
melt_quote.paid_time = int(time.time())
|
||||||
await self.crud.update_melt_quote(quote=melt_quote, db=self.db)
|
await self.crud.update_melt_quote(quote=melt_quote, db=self.db)
|
||||||
await self.events.submit(melt_quote)
|
await self.events.submit(melt_quote)
|
||||||
|
|||||||
@@ -773,3 +773,45 @@ async def m019_add_fee_to_keysets(db: Database):
|
|||||||
await conn.execute(
|
await conn.execute(
|
||||||
f"UPDATE {table_with_schema(db, 'keysets')} SET input_fee_ppk = 0"
|
f"UPDATE {table_with_schema(db, 'keysets')} SET input_fee_ppk = 0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def m020_add_state_to_mint_and_melt_quotes(db: Database):
|
||||||
|
async with db.connect() as conn:
|
||||||
|
await conn.execute(
|
||||||
|
f"ALTER TABLE {table_with_schema(db, 'mint_quotes')} ADD COLUMN state TEXT"
|
||||||
|
)
|
||||||
|
await conn.execute(
|
||||||
|
f"ALTER TABLE {table_with_schema(db, 'melt_quotes')} ADD COLUMN state TEXT"
|
||||||
|
)
|
||||||
|
|
||||||
|
# get all melt and mint quotes and figure out the state to set using the `paid` column
|
||||||
|
# and the `paid` and `issued` column respectively
|
||||||
|
# mint quotes:
|
||||||
|
async with db.connect() as conn:
|
||||||
|
rows = await conn.fetchall(
|
||||||
|
f"SELECT * FROM {table_with_schema(db, 'mint_quotes')}"
|
||||||
|
)
|
||||||
|
for row in rows:
|
||||||
|
if row["issued"]:
|
||||||
|
state = "issued"
|
||||||
|
elif row["paid"]:
|
||||||
|
state = "paid"
|
||||||
|
else:
|
||||||
|
state = "unpaid"
|
||||||
|
await conn.execute(
|
||||||
|
f"UPDATE {table_with_schema(db, 'mint_quotes')} SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# melt quotes:
|
||||||
|
async with db.connect() as conn:
|
||||||
|
rows = await conn.fetchall(
|
||||||
|
f"SELECT * FROM {table_with_schema(db, 'melt_quotes')}"
|
||||||
|
)
|
||||||
|
for row in rows:
|
||||||
|
if row["paid"]:
|
||||||
|
state = "paid"
|
||||||
|
else:
|
||||||
|
state = "unpaid"
|
||||||
|
await conn.execute(
|
||||||
|
f"UPDATE {table_with_schema(db, 'melt_quotes')} SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
)
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ async def mint_quote(
|
|||||||
request=quote.request,
|
request=quote.request,
|
||||||
quote=quote.quote,
|
quote=quote.quote,
|
||||||
paid=quote.paid,
|
paid=quote.paid,
|
||||||
|
state=quote.state.value,
|
||||||
expiry=quote.expiry,
|
expiry=quote.expiry,
|
||||||
)
|
)
|
||||||
logger.trace(f"< POST /v1/mint/quote/bolt11: {resp}")
|
logger.trace(f"< POST /v1/mint/quote/bolt11: {resp}")
|
||||||
@@ -184,6 +185,7 @@ async def get_mint_quote(request: Request, quote: str) -> PostMintQuoteResponse:
|
|||||||
quote=mint_quote.quote,
|
quote=mint_quote.quote,
|
||||||
request=mint_quote.request,
|
request=mint_quote.request,
|
||||||
paid=mint_quote.paid,
|
paid=mint_quote.paid,
|
||||||
|
state=mint_quote.state.value,
|
||||||
expiry=mint_quote.expiry,
|
expiry=mint_quote.expiry,
|
||||||
)
|
)
|
||||||
logger.trace(f"< GET /v1/mint/quote/bolt11/{quote}")
|
logger.trace(f"< GET /v1/mint/quote/bolt11/{quote}")
|
||||||
@@ -274,6 +276,7 @@ async def get_melt_quote(request: Request, quote: str) -> PostMeltQuoteResponse:
|
|||||||
amount=melt_quote.amount,
|
amount=melt_quote.amount,
|
||||||
fee_reserve=melt_quote.fee_reserve,
|
fee_reserve=melt_quote.fee_reserve,
|
||||||
paid=melt_quote.paid,
|
paid=melt_quote.paid,
|
||||||
|
state=melt_quote.state.value,
|
||||||
expiry=melt_quote.expiry,
|
expiry=melt_quote.expiry,
|
||||||
)
|
)
|
||||||
logger.trace(f"< GET /v1/melt/quote/bolt11/{quote}")
|
logger.trace(f"< GET /v1/melt/quote/bolt11/{quote}")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Dict, List, Optional
|
|||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from ..core.base import BlindedMessage, BlindedSignature, SpentState
|
from ..core.base import BlindedMessage, BlindedSignature, ProofSpentState
|
||||||
from ..core.errors import CashuError
|
from ..core.errors import CashuError
|
||||||
from ..core.models import (
|
from ..core.models import (
|
||||||
CheckFeesRequest_deprecated,
|
CheckFeesRequest_deprecated,
|
||||||
@@ -345,13 +345,13 @@ async def check_spendable_deprecated(
|
|||||||
spendableList: List[bool] = []
|
spendableList: List[bool] = []
|
||||||
pendingList: List[bool] = []
|
pendingList: List[bool] = []
|
||||||
for proof_state in proofs_state:
|
for proof_state in proofs_state:
|
||||||
if proof_state.state == SpentState.unspent:
|
if proof_state.state == ProofSpentState.unspent:
|
||||||
spendableList.append(True)
|
spendableList.append(True)
|
||||||
pendingList.append(False)
|
pendingList.append(False)
|
||||||
elif proof_state.state == SpentState.spent:
|
elif proof_state.state == ProofSpentState.spent:
|
||||||
spendableList.append(False)
|
spendableList.append(False)
|
||||||
pendingList.append(False)
|
pendingList.append(False)
|
||||||
elif proof_state.state == SpentState.pending:
|
elif proof_state.state == ProofSpentState.pending:
|
||||||
spendableList.append(True)
|
spendableList.append(True)
|
||||||
pendingList.append(True)
|
pendingList.append(True)
|
||||||
return CheckSpendableResponse_deprecated(
|
return CheckSpendableResponse_deprecated(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Mapping
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from ..core.base import Method, Unit
|
from ..core.base import Method, MintQuoteState, Unit
|
||||||
from ..core.db import Database
|
from ..core.db import Database
|
||||||
from ..lightning.base import LightningBackend
|
from ..lightning.base import LightningBackend
|
||||||
from ..mint.crud import LedgerCrud
|
from ..mint.crud import LedgerCrud
|
||||||
@@ -40,6 +40,7 @@ class LedgerTasks(SupportsDb, SupportsBackends, SupportsEvents):
|
|||||||
# set the quote as paid
|
# set the quote as paid
|
||||||
if not quote.paid:
|
if not quote.paid:
|
||||||
quote.paid = True
|
quote.paid = True
|
||||||
|
quote.state = MintQuoteState.paid
|
||||||
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
await self.crud.update_mint_quote(quote=quote, db=self.db)
|
||||||
logger.trace(f"Quote {quote} set as paid and ")
|
logger.trace(f"Quote {quote} set as paid and ")
|
||||||
await self.events.submit(quote)
|
await self.events.submit(quote)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import click
|
|||||||
from click import Context
|
from click import Context
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from ...core.base import Invoice, Method, TokenV3, Unit
|
from ...core.base import Invoice, Method, MintQuoteState, TokenV3, Unit
|
||||||
from ...core.helpers import sum_proofs
|
from ...core.helpers import sum_proofs
|
||||||
from ...core.json_rpc.base import JSONRPCNotficationParams
|
from ...core.json_rpc.base import JSONRPCNotficationParams
|
||||||
from ...core.logging import configure_logger
|
from ...core.logging import configure_logger
|
||||||
@@ -296,8 +296,10 @@ async def invoice(ctx: Context, amount: float, id: str, split: int, no_check: bo
|
|||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
logger.debug(f"Received callback for quote: {quote}")
|
logger.debug(f"Received callback for quote: {quote}")
|
||||||
|
# we need to sleep to give the callback map some time to be populated
|
||||||
|
time.sleep(0.1)
|
||||||
if (
|
if (
|
||||||
quote.paid
|
(quote.paid or quote.state == MintQuoteState.paid.value)
|
||||||
and quote.request == invoice.bolt11
|
and quote.request == invoice.bolt11
|
||||||
and msg.subId in subscription.callback_map.keys()
|
and msg.subId in subscription.callback_map.keys()
|
||||||
):
|
):
|
||||||
@@ -310,6 +312,9 @@ async def invoice(ctx: Context, amount: float, id: str, split: int, no_check: bo
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error during mint: {str(e)}")
|
print(f"Error during mint: {str(e)}")
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
logger.debug("Quote not paid yet.")
|
||||||
|
return
|
||||||
|
|
||||||
# user requests an invoice
|
# user requests an invoice
|
||||||
if amount and not id:
|
if amount and not id:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bolt11
|
import bolt11
|
||||||
|
|
||||||
from ...core.base import Amount, SpentState, Unit
|
from ...core.base import Amount, ProofSpentState, Unit
|
||||||
from ...core.helpers import sum_promises
|
from ...core.helpers import sum_promises
|
||||||
from ...core.settings import settings
|
from ...core.settings import settings
|
||||||
from ...lightning.base import (
|
from ...lightning.base import (
|
||||||
@@ -131,12 +131,12 @@ class LightningWallet(Wallet):
|
|||||||
if not proofs_states:
|
if not proofs_states:
|
||||||
return PaymentStatus(paid=False) # "states not fount"
|
return PaymentStatus(paid=False) # "states not fount"
|
||||||
|
|
||||||
if all([p.state == SpentState.pending for p in proofs_states.states]):
|
if all([p.state == ProofSpentState.pending for p in proofs_states.states]):
|
||||||
return PaymentStatus(paid=None) # "pending (with check)"
|
return PaymentStatus(paid=None) # "pending (with check)"
|
||||||
if any([p.state == SpentState.spent for p in proofs_states.states]):
|
if any([p.state == ProofSpentState.spent for p in proofs_states.states]):
|
||||||
# NOTE: consider adding this check in wallet.py and mark the invoice as paid if all proofs are spent
|
# NOTE: consider adding this check in wallet.py and mark the invoice as paid if all proofs are spent
|
||||||
return PaymentStatus(paid=True) # "paid (with check)"
|
return PaymentStatus(paid=True) # "paid (with check)"
|
||||||
if all([p.state == SpentState.unspent for p in proofs_states.states]):
|
if all([p.state == ProofSpentState.unspent for p in proofs_states.states]):
|
||||||
return PaymentStatus(paid=False) # "failed (with check)"
|
return PaymentStatus(paid=False) # "failed (with check)"
|
||||||
return PaymentStatus(paid=None) # "undefined state"
|
return PaymentStatus(paid=None) # "undefined state"
|
||||||
|
|
||||||
|
|||||||
@@ -243,3 +243,81 @@ async def m012_add_fee_to_keysets(db: Database):
|
|||||||
# add column for storing the fee of a keyset
|
# add column for storing the fee of a keyset
|
||||||
await conn.execute("ALTER TABLE keysets ADD COLUMN input_fee_ppk INTEGER")
|
await conn.execute("ALTER TABLE keysets ADD COLUMN input_fee_ppk INTEGER")
|
||||||
await conn.execute("UPDATE keysets SET input_fee_ppk = 0")
|
await conn.execute("UPDATE keysets SET input_fee_ppk = 0")
|
||||||
|
|
||||||
|
|
||||||
|
# # async def m020_add_state_to_mint_and_melt_quotes(db: Database):
|
||||||
|
# # async with db.connect() as conn:
|
||||||
|
# # await conn.execute(
|
||||||
|
# # f"ALTER TABLE {table_with_schema(db, 'mint_quotes')} ADD COLUMN state TEXT"
|
||||||
|
# # )
|
||||||
|
# # await conn.execute(
|
||||||
|
# # f"ALTER TABLE {table_with_schema(db, 'melt_quotes')} ADD COLUMN state TEXT"
|
||||||
|
# # )
|
||||||
|
|
||||||
|
# # # get all melt and mint quotes and figure out the state to set using the `paid` column
|
||||||
|
# # # and the `paid` and `issued` column respectively
|
||||||
|
# # # mint quotes:
|
||||||
|
# # async with db.connect() as conn:
|
||||||
|
# # rows = await conn.fetchall(
|
||||||
|
# # f"SELECT * FROM {table_with_schema(db, 'mint_quotes')}"
|
||||||
|
# # )
|
||||||
|
# # for row in rows:
|
||||||
|
# # if row["issued"]:
|
||||||
|
# # state = "issued"
|
||||||
|
# # elif row["paid"]:
|
||||||
|
# # state = "paid"
|
||||||
|
# # else:
|
||||||
|
# # state = "unpaid"
|
||||||
|
# # await conn.execute(
|
||||||
|
# # f"UPDATE {table_with_schema(db, 'mint_quotes')} SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
# # )
|
||||||
|
|
||||||
|
# # # melt quotes:
|
||||||
|
# # async with db.connect() as conn:
|
||||||
|
# # rows = await conn.fetchall(
|
||||||
|
# # f"SELECT * FROM {table_with_schema(db, 'melt_quotes')}"
|
||||||
|
# # )
|
||||||
|
# # for row in rows:
|
||||||
|
# # if row["paid"]:
|
||||||
|
# # state = "paid"
|
||||||
|
# # else:
|
||||||
|
# # state = "unpaid"
|
||||||
|
# # await conn.execute(
|
||||||
|
# # f"UPDATE {table_with_schema(db, 'melt_quotes')} SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
# # )
|
||||||
|
# # add the equivalent of the above migration for the wallet here. do not use table_with_schema. use the tables and columns
|
||||||
|
# # as they are defined in the wallet db
|
||||||
|
|
||||||
|
|
||||||
|
# async def m020_add_state_to_mint_and_melt_quotes(db: Database):
|
||||||
|
# async with db.connect() as conn:
|
||||||
|
# await conn.execute("ALTER TABLE mint_quotes ADD COLUMN state TEXT")
|
||||||
|
# await conn.execute("ALTER TABLE melt_quotes ADD COLUMN state TEXT")
|
||||||
|
|
||||||
|
# # get all melt and mint quotes and figure out the state to set using the `paid` column
|
||||||
|
# # and the `paid` and `issued` column respectively
|
||||||
|
# # mint quotes:
|
||||||
|
# async with db.connect() as conn:
|
||||||
|
# rows = await conn.fetchall("SELECT * FROM mint_quotes")
|
||||||
|
# for row in rows:
|
||||||
|
# if row["issued"]:
|
||||||
|
# state = "issued"
|
||||||
|
# elif row["paid"]:
|
||||||
|
# state = "paid"
|
||||||
|
# else:
|
||||||
|
# state = "unpaid"
|
||||||
|
# await conn.execute(
|
||||||
|
# f"UPDATE mint_quotes SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# # melt quotes:
|
||||||
|
# async with db.connect() as conn:
|
||||||
|
# rows = await conn.fetchall("SELECT * FROM melt_quotes")
|
||||||
|
# for row in rows:
|
||||||
|
# if row["paid"]:
|
||||||
|
# state = "paid"
|
||||||
|
# else:
|
||||||
|
# state = "unpaid"
|
||||||
|
# await conn.execute(
|
||||||
|
# f"UPDATE melt_quotes SET state = '{state}' WHERE quote = '{row['quote']}'"
|
||||||
|
# )
|
||||||
|
|||||||
@@ -47,14 +47,19 @@ class SubscriptionManager:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
msg = JSONRPCNotification.parse_raw(message)
|
msg = JSONRPCNotification.parse_raw(message)
|
||||||
params = JSONRPCNotficationParams.parse_obj(msg.params)
|
|
||||||
logger.debug(f"Received notification: {msg}")
|
logger.debug(f"Received notification: {msg}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing notification: {e}")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
params = JSONRPCNotficationParams.parse_obj(msg.params)
|
||||||
|
logger.trace(f"Notification params: {params}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing notification params: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
self.callback_map[params.subId](params)
|
self.callback_map[params.subId](params)
|
||||||
return
|
return
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
logger.error(f"Error parsing message: {message}")
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.websocket.run_forever(ping_interval=10, ping_timeout=5)
|
self.websocket.run_forever(ping_interval=10, ping_timeout=5)
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ from loguru import logger
|
|||||||
from ..core.base import (
|
from ..core.base import (
|
||||||
BlindedMessage,
|
BlindedMessage,
|
||||||
BlindedSignature,
|
BlindedSignature,
|
||||||
|
MeltQuoteState,
|
||||||
Proof,
|
Proof,
|
||||||
|
ProofSpentState,
|
||||||
ProofState,
|
ProofState,
|
||||||
SpentState,
|
|
||||||
Unit,
|
Unit,
|
||||||
WalletKeyset,
|
WalletKeyset,
|
||||||
)
|
)
|
||||||
@@ -390,6 +391,7 @@ class LedgerAPI(LedgerAPIDeprecated, object):
|
|||||||
amount=amount or invoice_obj.amount_msat // 1000,
|
amount=amount or invoice_obj.amount_msat // 1000,
|
||||||
fee_reserve=ret.fee or 0,
|
fee_reserve=ret.fee or 0,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MeltQuoteState.unpaid.value,
|
||||||
expiry=invoice_obj.expiry,
|
expiry=invoice_obj.expiry,
|
||||||
)
|
)
|
||||||
# END backwards compatibility < 0.15.0
|
# END backwards compatibility < 0.15.0
|
||||||
@@ -509,11 +511,11 @@ class LedgerAPI(LedgerAPIDeprecated, object):
|
|||||||
states: List[ProofState] = []
|
states: List[ProofState] = []
|
||||||
for spendable, pending, p in zip(ret.spendable, ret.pending, proofs):
|
for spendable, pending, p in zip(ret.spendable, ret.pending, proofs):
|
||||||
if spendable and not pending:
|
if spendable and not pending:
|
||||||
states.append(ProofState(Y=p.Y, state=SpentState.unspent))
|
states.append(ProofState(Y=p.Y, state=ProofSpentState.unspent))
|
||||||
elif spendable and pending:
|
elif spendable and pending:
|
||||||
states.append(ProofState(Y=p.Y, state=SpentState.pending))
|
states.append(ProofState(Y=p.Y, state=ProofSpentState.pending))
|
||||||
else:
|
else:
|
||||||
states.append(ProofState(Y=p.Y, state=SpentState.spent))
|
states.append(ProofState(Y=p.Y, state=ProofSpentState.spent))
|
||||||
ret = PostCheckStateResponse(states=states)
|
ret = PostCheckStateResponse(states=states)
|
||||||
return ret
|
return ret
|
||||||
# END backwards compatibility < 0.15.0
|
# END backwards compatibility < 0.15.0
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from ..core.base import (
|
|||||||
DLEQWallet,
|
DLEQWallet,
|
||||||
Invoice,
|
Invoice,
|
||||||
Proof,
|
Proof,
|
||||||
SpentState,
|
ProofSpentState,
|
||||||
Unit,
|
Unit,
|
||||||
WalletKeyset,
|
WalletKeyset,
|
||||||
)
|
)
|
||||||
@@ -924,7 +924,7 @@ class Wallet(
|
|||||||
if check_spendable:
|
if check_spendable:
|
||||||
proof_states = await self.check_proof_state(proofs)
|
proof_states = await self.check_proof_state(proofs)
|
||||||
for i, state in enumerate(proof_states.states):
|
for i, state in enumerate(proof_states.states):
|
||||||
if state.state == SpentState.spent:
|
if state.state == ProofSpentState.spent:
|
||||||
invalidated_proofs.append(proofs[i])
|
invalidated_proofs.append(proofs[i])
|
||||||
else:
|
else:
|
||||||
invalidated_proofs = proofs
|
invalidated_proofs = proofs
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from ..core.base import (
|
|||||||
BlindedMessage,
|
BlindedMessage,
|
||||||
BlindedMessage_Deprecated,
|
BlindedMessage_Deprecated,
|
||||||
BlindedSignature,
|
BlindedSignature,
|
||||||
|
MintQuoteState,
|
||||||
Proof,
|
Proof,
|
||||||
WalletKeyset,
|
WalletKeyset,
|
||||||
)
|
)
|
||||||
@@ -252,6 +253,7 @@ class LedgerAPIDeprecated(SupportsHttpxClient, SupportsMintURL):
|
|||||||
quote=mint_response.hash,
|
quote=mint_response.hash,
|
||||||
request=mint_response.pr,
|
request=mint_response.pr,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MintQuoteState.unpaid.value,
|
||||||
expiry=decoded_invoice.date + (decoded_invoice.expiry or 0),
|
expiry=decoded_invoice.date + (decoded_invoice.expiry or 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ settings.tor = False
|
|||||||
settings.wallet_unit = "sat"
|
settings.wallet_unit = "sat"
|
||||||
settings.mint_backend_bolt11_sat = settings.mint_backend_bolt11_sat or "FakeWallet"
|
settings.mint_backend_bolt11_sat = settings.mint_backend_bolt11_sat or "FakeWallet"
|
||||||
settings.fakewallet_brr = True
|
settings.fakewallet_brr = True
|
||||||
settings.fakewallet_delay_outgoing_payment = None
|
settings.fakewallet_delay_outgoing_payment = 0
|
||||||
settings.fakewallet_delay_incoming_payment = 1
|
settings.fakewallet_delay_incoming_payment = 1
|
||||||
settings.fakewallet_stochastic_invoice = False
|
settings.fakewallet_stochastic_invoice = False
|
||||||
assert (
|
assert (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import httpx
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import SpentState
|
from cashu.core.base import ProofSpentState
|
||||||
from cashu.core.models import (
|
from cashu.core.models import (
|
||||||
GetInfoResponse,
|
GetInfoResponse,
|
||||||
MintMeltMethodSetting,
|
MintMeltMethodSetting,
|
||||||
@@ -403,7 +403,7 @@ async def test_api_check_state(ledger: Ledger):
|
|||||||
response = PostCheckStateResponse.parse_obj(response.json())
|
response = PostCheckStateResponse.parse_obj(response.json())
|
||||||
assert response
|
assert response
|
||||||
assert len(response.states) == 2
|
assert len(response.states) == 2
|
||||||
assert response.states[0].state == SpentState.unspent
|
assert response.states[0].state == ProofSpentState.unspent
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import bolt11
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import MeltQuote, Proof, SpentState
|
from cashu.core.base import MeltQuote, MeltQuoteState, Proof, ProofSpentState
|
||||||
from cashu.core.crypto.aes import AESCipher
|
from cashu.core.crypto.aes import AESCipher
|
||||||
from cashu.core.db import Database
|
from cashu.core.db import Database
|
||||||
from cashu.core.settings import settings
|
from cashu.core.settings import settings
|
||||||
@@ -144,6 +144,7 @@ async def create_pending_melts(
|
|||||||
checking_id=check_id,
|
checking_id=check_id,
|
||||||
unit="sat",
|
unit="sat",
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MeltQuoteState.unpaid,
|
||||||
amount=100,
|
amount=100,
|
||||||
fee_reserve=1,
|
fee_reserve=1,
|
||||||
)
|
)
|
||||||
@@ -172,7 +173,7 @@ async def test_startup_fakewallet_pending_quote_success(ledger: Ledger):
|
|||||||
after the startup routine determines that the associated melt quote was paid."""
|
after the startup routine determines that the associated melt quote was paid."""
|
||||||
pending_proof, quote = await create_pending_melts(ledger)
|
pending_proof, quote = await create_pending_melts(ledger)
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.pending
|
assert states[0].state == ProofSpentState.pending
|
||||||
settings.fakewallet_payment_state = True
|
settings.fakewallet_payment_state = True
|
||||||
# run startup routinge
|
# run startup routinge
|
||||||
await ledger.startup_ledger()
|
await ledger.startup_ledger()
|
||||||
@@ -185,7 +186,7 @@ async def test_startup_fakewallet_pending_quote_success(ledger: Ledger):
|
|||||||
|
|
||||||
# expect that proofs are spent
|
# expect that proofs are spent
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.spent
|
assert states[0].state == ProofSpentState.spent
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -198,7 +199,7 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
|
|||||||
"""
|
"""
|
||||||
pending_proof, quote = await create_pending_melts(ledger)
|
pending_proof, quote = await create_pending_melts(ledger)
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.pending
|
assert states[0].state == ProofSpentState.pending
|
||||||
settings.fakewallet_payment_state = False
|
settings.fakewallet_payment_state = False
|
||||||
# run startup routinge
|
# run startup routinge
|
||||||
await ledger.startup_ledger()
|
await ledger.startup_ledger()
|
||||||
@@ -211,7 +212,7 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
|
|||||||
|
|
||||||
# expect that proofs are unspent
|
# expect that proofs are unspent
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.unspent
|
assert states[0].state == ProofSpentState.unspent
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -219,7 +220,7 @@ async def test_startup_fakewallet_pending_quote_failure(ledger: Ledger):
|
|||||||
async def test_startup_fakewallet_pending_quote_pending(ledger: Ledger):
|
async def test_startup_fakewallet_pending_quote_pending(ledger: Ledger):
|
||||||
pending_proof, quote = await create_pending_melts(ledger)
|
pending_proof, quote = await create_pending_melts(ledger)
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.pending
|
assert states[0].state == ProofSpentState.pending
|
||||||
settings.fakewallet_payment_state = None
|
settings.fakewallet_payment_state = None
|
||||||
# run startup routinge
|
# run startup routinge
|
||||||
await ledger.startup_ledger()
|
await ledger.startup_ledger()
|
||||||
@@ -232,7 +233,7 @@ async def test_startup_fakewallet_pending_quote_pending(ledger: Ledger):
|
|||||||
|
|
||||||
# expect that proofs are still pending
|
# expect that proofs are still pending
|
||||||
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
states = await ledger.db_read.get_proofs_states([pending_proof.Y])
|
||||||
assert states[0].state == SpentState.pending
|
assert states[0].state == ProofSpentState.pending
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -274,7 +275,7 @@ async def test_startup_regtest_pending_quote_pending(wallet: Wallet, ledger: Led
|
|||||||
|
|
||||||
# expect that proofs are still pending
|
# expect that proofs are still pending
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.pending for s in states])
|
assert all([s.state == ProofSpentState.pending for s in states])
|
||||||
|
|
||||||
# only now settle the invoice
|
# only now settle the invoice
|
||||||
settle_invoice(preimage=preimage)
|
settle_invoice(preimage=preimage)
|
||||||
@@ -308,7 +309,7 @@ async def test_startup_regtest_pending_quote_success(wallet: Wallet, ledger: Led
|
|||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
# expect that proofs are pending
|
# expect that proofs are pending
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.pending for s in states])
|
assert all([s.state == ProofSpentState.pending for s in states])
|
||||||
|
|
||||||
settle_invoice(preimage=preimage)
|
settle_invoice(preimage=preimage)
|
||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
@@ -324,7 +325,7 @@ async def test_startup_regtest_pending_quote_success(wallet: Wallet, ledger: Led
|
|||||||
|
|
||||||
# expect that proofs are spent
|
# expect that proofs are spent
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.spent for s in states])
|
assert all([s.state == ProofSpentState.spent for s in states])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -359,7 +360,7 @@ async def test_startup_regtest_pending_quote_failure(wallet: Wallet, ledger: Led
|
|||||||
|
|
||||||
# expect that proofs are pending
|
# expect that proofs are pending
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.pending for s in states])
|
assert all([s.state == ProofSpentState.pending for s in states])
|
||||||
|
|
||||||
cancel_invoice(preimage_hash=preimage_hash)
|
cancel_invoice(preimage_hash=preimage_hash)
|
||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
@@ -375,4 +376,4 @@ async def test_startup_regtest_pending_quote_failure(wallet: Wallet, ledger: Led
|
|||||||
|
|
||||||
# expect that proofs are unspent
|
# expect that proofs are unspent
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.unspent for s in states])
|
assert all([s.state == ProofSpentState.unspent for s in states])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import pytest
|
|||||||
import respx
|
import respx
|
||||||
from httpx import Response
|
from httpx import Response
|
||||||
|
|
||||||
from cashu.core.base import Amount, MeltQuote, Unit
|
from cashu.core.base import Amount, MeltQuote, MeltQuoteState, Unit
|
||||||
from cashu.core.models import PostMeltQuoteRequest
|
from cashu.core.models import PostMeltQuoteRequest
|
||||||
from cashu.core.settings import settings
|
from cashu.core.settings import settings
|
||||||
from cashu.lightning.blink import MINIMUM_FEE_MSAT, BlinkWallet # type: ignore
|
from cashu.lightning.blink import MINIMUM_FEE_MSAT, BlinkWallet # type: ignore
|
||||||
@@ -99,6 +99,7 @@ async def test_blink_pay_invoice():
|
|||||||
amount=100,
|
amount=100,
|
||||||
fee_reserve=12,
|
fee_reserve=12,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MeltQuoteState.unpaid,
|
||||||
)
|
)
|
||||||
payment = await blink.pay_invoice(quote, 1000)
|
payment = await blink.pay_invoice(quote, 1000)
|
||||||
assert payment.ok
|
assert payment.ok
|
||||||
@@ -131,6 +132,7 @@ async def test_blink_pay_invoice_failure():
|
|||||||
amount=100,
|
amount=100,
|
||||||
fee_reserve=12,
|
fee_reserve=12,
|
||||||
paid=False,
|
paid=False,
|
||||||
|
state=MeltQuoteState.unpaid,
|
||||||
)
|
)
|
||||||
payment = await blink.pay_invoice(quote, 1000)
|
payment = await blink.pay_invoice(quote, 1000)
|
||||||
assert not payment.ok
|
assert not payment.ok
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import asyncio
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import SpentState
|
from cashu.core.base import ProofSpentState
|
||||||
from cashu.mint.ledger import Ledger
|
from cashu.mint.ledger import Ledger
|
||||||
from cashu.wallet.wallet import Wallet
|
from cashu.wallet.wallet import Wallet
|
||||||
from tests.conftest import SERVER_ENDPOINT
|
from tests.conftest import SERVER_ENDPOINT
|
||||||
@@ -63,7 +63,7 @@ async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger):
|
|||||||
|
|
||||||
# expect that proofs are still pending
|
# expect that proofs are still pending
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.pending for s in states])
|
assert all([s.state == ProofSpentState.pending for s in states])
|
||||||
|
|
||||||
# only now settle the invoice
|
# only now settle the invoice
|
||||||
settle_invoice(preimage=preimage)
|
settle_invoice(preimage=preimage)
|
||||||
@@ -71,7 +71,7 @@ async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger):
|
|||||||
|
|
||||||
# expect that proofs are now spent
|
# expect that proofs are now spent
|
||||||
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
states = await ledger.db_read.get_proofs_states([p.Y for p in send_proofs])
|
||||||
assert all([s.state == SpentState.spent for s in states])
|
assert all([s.state == ProofSpentState.spent for s in states])
|
||||||
|
|
||||||
# expect that no melt quote is pending
|
# expect that no melt quote is pending
|
||||||
melt_quotes = await ledger.crud.get_all_melt_quotes_from_pending_proofs(
|
melt_quotes = await ledger.crud.get_all_melt_quotes_from_pending_proofs(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import List
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import Proof, SpentState
|
from cashu.core.base import Proof, ProofSpentState
|
||||||
from cashu.core.crypto.secp import PrivateKey, PublicKey
|
from cashu.core.crypto.secp import PrivateKey, PublicKey
|
||||||
from cashu.core.migrations import migrate_databases
|
from cashu.core.migrations import migrate_databases
|
||||||
from cashu.core.p2pk import SigFlags
|
from cashu.core.p2pk import SigFlags
|
||||||
@@ -80,7 +80,7 @@ async def test_p2pk(wallet1: Wallet, wallet2: Wallet):
|
|||||||
await wallet2.redeem(send_proofs)
|
await wallet2.redeem(send_proofs)
|
||||||
|
|
||||||
proof_states = await wallet2.check_proof_state(send_proofs)
|
proof_states = await wallet2.check_proof_state(send_proofs)
|
||||||
assert all([p.state == SpentState.spent for p in proof_states.states])
|
assert all([p.state == ProofSpentState.spent for p in proof_states.states])
|
||||||
|
|
||||||
if not is_deprecated_api_only:
|
if not is_deprecated_api_only:
|
||||||
for state in proof_states.states:
|
for state in proof_states.states:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import bolt11
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import SpentState
|
from cashu.core.base import ProofSpentState
|
||||||
from cashu.mint.ledger import Ledger
|
from cashu.mint.ledger import Ledger
|
||||||
from cashu.wallet.wallet import Wallet
|
from cashu.wallet.wallet import Wallet
|
||||||
from tests.conftest import SERVER_ENDPOINT
|
from tests.conftest import SERVER_ENDPOINT
|
||||||
@@ -57,14 +57,14 @@ async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger):
|
|||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
|
|
||||||
states = await wallet.check_proof_state(send_proofs)
|
states = await wallet.check_proof_state(send_proofs)
|
||||||
assert all([s.state == SpentState.pending for s in states.states])
|
assert all([s.state == ProofSpentState.pending for s in states.states])
|
||||||
|
|
||||||
settle_invoice(preimage=preimage)
|
settle_invoice(preimage=preimage)
|
||||||
|
|
||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
|
|
||||||
states = await wallet.check_proof_state(send_proofs)
|
states = await wallet.check_proof_state(send_proofs)
|
||||||
assert all([s.state == SpentState.spent for s in states.states])
|
assert all([s.state == ProofSpentState.spent for s in states.states])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -97,11 +97,11 @@ async def test_regtest_failed_quote(wallet: Wallet, ledger: Ledger):
|
|||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
|
|
||||||
states = await wallet.check_proof_state(send_proofs)
|
states = await wallet.check_proof_state(send_proofs)
|
||||||
assert all([s.state == SpentState.pending for s in states.states])
|
assert all([s.state == ProofSpentState.pending for s in states.states])
|
||||||
|
|
||||||
cancel_invoice(preimage_hash=preimage_hash)
|
cancel_invoice(preimage_hash=preimage_hash)
|
||||||
|
|
||||||
await asyncio.sleep(SLEEP_TIME)
|
await asyncio.sleep(SLEEP_TIME)
|
||||||
|
|
||||||
states = await wallet.check_proof_state(send_proofs)
|
states = await wallet.check_proof_state(send_proofs)
|
||||||
assert all([s.state == SpentState.unspent for s in states.states])
|
assert all([s.state == ProofSpentState.unspent for s in states.states])
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import asyncio
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import Method, ProofState
|
from cashu.core.base import Method, MintQuoteState, ProofSpentState, ProofState
|
||||||
from cashu.core.json_rpc.base import JSONRPCNotficationParams
|
from cashu.core.json_rpc.base import JSONRPCNotficationParams
|
||||||
from cashu.core.nuts import WEBSOCKETS_NUT
|
from cashu.core.nuts import WEBSOCKETS_NUT
|
||||||
from cashu.core.settings import settings
|
from cashu.core.settings import settings
|
||||||
@@ -51,20 +51,17 @@ async def test_wallet_subscription_mint(wallet: Wallet):
|
|||||||
wait = settings.fakewallet_delay_incoming_payment or 2
|
wait = settings.fakewallet_delay_incoming_payment or 2
|
||||||
await asyncio.sleep(wait + 2)
|
await asyncio.sleep(wait + 2)
|
||||||
|
|
||||||
# TODO: check for pending and paid states according to: https://github.com/cashubtc/nuts/pull/136
|
|
||||||
# TODO: we have three messages here, but the value "paid" only changes once
|
|
||||||
# the mint sends an update when the quote is pending but the API does not express that yet
|
|
||||||
|
|
||||||
# first we expect the issued=False state to arrive
|
|
||||||
|
|
||||||
assert triggered
|
assert triggered
|
||||||
assert len(msg_stack) == 3
|
assert len(msg_stack) == 3
|
||||||
|
|
||||||
assert msg_stack[0].payload["paid"] is False
|
assert msg_stack[0].payload["paid"] is False
|
||||||
|
assert msg_stack[0].payload["state"] == MintQuoteState.unpaid.value
|
||||||
|
|
||||||
assert msg_stack[1].payload["paid"] is True
|
assert msg_stack[1].payload["paid"] is True
|
||||||
|
assert msg_stack[1].payload["state"] == MintQuoteState.paid.value
|
||||||
|
|
||||||
assert msg_stack[2].payload["paid"] is True
|
assert msg_stack[2].payload["paid"] is True
|
||||||
|
assert msg_stack[2].payload["state"] == MintQuoteState.issued.value
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -103,16 +100,16 @@ async def test_wallet_subscription_swap(wallet: Wallet):
|
|||||||
pending_stack = msg_stack[:n_subscriptions]
|
pending_stack = msg_stack[:n_subscriptions]
|
||||||
for msg in pending_stack:
|
for msg in pending_stack:
|
||||||
proof_state = ProofState.parse_obj(msg.payload)
|
proof_state = ProofState.parse_obj(msg.payload)
|
||||||
assert proof_state.state.value == "UNSPENT"
|
assert proof_state.state == ProofSpentState.unspent
|
||||||
|
|
||||||
# the second one is the PENDING state
|
# the second one is the PENDING state
|
||||||
spent_stack = msg_stack[n_subscriptions : n_subscriptions * 2]
|
spent_stack = msg_stack[n_subscriptions : n_subscriptions * 2]
|
||||||
for msg in spent_stack:
|
for msg in spent_stack:
|
||||||
proof_state = ProofState.parse_obj(msg.payload)
|
proof_state = ProofState.parse_obj(msg.payload)
|
||||||
assert proof_state.state.value == "PENDING"
|
assert proof_state.state == ProofSpentState.pending
|
||||||
|
|
||||||
# the third one is the SPENT state
|
# the third one is the SPENT state
|
||||||
spent_stack = msg_stack[n_subscriptions * 2 :]
|
spent_stack = msg_stack[n_subscriptions * 2 :]
|
||||||
for msg in spent_stack:
|
for msg in spent_stack:
|
||||||
proof_state = ProofState.parse_obj(msg.payload)
|
proof_state = ProofState.parse_obj(msg.payload)
|
||||||
assert proof_state.state.value == "SPENT"
|
assert proof_state.state == ProofSpentState.spent
|
||||||
|
|||||||
Reference in New Issue
Block a user