Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 0 additions & 7 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,6 @@ def from_row(cls, row: Row, change: Optional[List[BlindedSignature]] = None):

@classmethod
def from_resp_wallet(cls, melt_quote_resp, mint: str, unit: str, request: str):
# BEGIN: BACKWARDS COMPATIBILITY < 0.16.0: "paid" field to "state"
if melt_quote_resp.state is None:
if melt_quote_resp.paid is True:
melt_quote_resp.state = MeltQuoteState.paid
elif melt_quote_resp.paid is False:
melt_quote_resp.state = MeltQuoteState.unpaid
# END: BACKWARDS COMPATIBILITY < 0.16.0
return cls(
quote=melt_quote_resp.quote,
method="bolt11",
Comment thread
KvngMikey marked this conversation as resolved.
Expand Down
5 changes: 0 additions & 5 deletions cashu/core/models/melt_quote.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ class PostMeltQuoteResponse(BaseModel):
str
] # output payment request (optional for BACKWARDS COMPAT mint response < 0.17.0)
fee_reserve: int # input fee reserve
paid: Optional[bool] = (
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 @@ -60,6 +57,4 @@ def from_melt_quote(cls, melt_quote: MeltQuote) -> "PostMeltQuoteResponse":
to_dict = melt_quote.model_dump()
# turn state into string
to_dict["state"] = melt_quote.state.value
# add deprecated "paid" field
to_dict["paid"] = melt_quote.paid
return cls.model_validate(to_dict)
12 changes: 5 additions & 7 deletions cashu/mint/auth/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@ async def store_melt_quote(
await (conn or db).execute(
f"""
INSERT INTO {db.table_with_schema('melt_quotes')}
(quote, method, request, checking_id, unit, amount, fee_reserve, paid, state, created_time, paid_time, fee_paid, proof, change, expiry)
VALUES (:quote, :method, :request, :checking_id, :unit, :amount, :fee_reserve, :paid, :state, :created_time, :paid_time, :fee_paid, :proof, :change, :expiry)
(quote, method, request, checking_id, unit, amount, fee_reserve, state, created_time, paid_time, fee_paid, proof, change, expiry)
VALUES (:quote, :method, :request, :checking_id, :unit, :amount, :fee_reserve, :state, :created_time, :paid_time, :fee_paid, :proof, :change, :expiry)
""",
{
"quote": quote.quote,
Expand All @@ -511,8 +511,7 @@ async def store_melt_quote(
"unit": quote.unit,
"amount": quote.amount,
"fee_reserve": quote.fee_reserve or 0,
"paid": quote.paid,
"state": quote.state.name,
"state": quote.state.value,
"created_time": db.to_timestamp(
db.timestamp_from_seconds(quote.created_time) or ""
),
Expand Down Expand Up @@ -571,11 +570,10 @@ async def update_melt_quote(
) -> None:
await (conn or db).execute(
f"""
UPDATE {db.table_with_schema('melt_quotes')} SET paid = :paid, state = :state, fee_paid = :fee_paid, paid_time = :paid_time, proof = :proof, change = :change WHERE quote = :quote
UPDATE {db.table_with_schema('melt_quotes')} SET state = :state, fee_paid = :fee_paid, paid_time = :paid_time, proof = :proof, change = :change WHERE quote = :quote
""",
{
"paid": quote.paid,
"state": quote.state.name,
"state": quote.state.value,
"fee_paid": quote.fee_paid,
"paid_time": db.to_timestamp(
db.timestamp_from_seconds(quote.paid_time) or ""
Expand Down
5 changes: 2 additions & 3 deletions cashu/mint/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,8 +758,8 @@ async def store_melt_quote(
await (conn or db).execute(
f"""
INSERT INTO {db.table_with_schema("melt_quotes")}
(quote, method, request, checking_id, unit, amount, fee_reserve, state, paid, created_time, paid_time, fee_paid, proof, expiry)
VALUES (:quote, :method, :request, :checking_id, :unit, :amount, :fee_reserve, :state, :paid, :created_time, :paid_time, :fee_paid, :proof, :expiry)
(quote, method, request, checking_id, unit, amount, fee_reserve, state, created_time, paid_time, fee_paid, proof, expiry)
VALUES (:quote, :method, :request, :checking_id, :unit, :amount, :fee_reserve, :state, :created_time, :paid_time, :fee_paid, :proof, :expiry)
""",
{
"quote": quote.quote,
Expand All @@ -770,7 +770,6 @@ async def store_melt_quote(
"amount": quote.amount,
"fee_reserve": quote.fee_reserve or 0,
"state": quote.state.value,
"paid": quote.paid, # this is deprecated! we need to store it because we have a NOT NULL constraint | we could also remove the column but sqlite doesn't support that (we would have to make a new table)
"created_time": db.to_timestamp(
db.timestamp_from_seconds(quote.created_time) or ""
),
Expand Down
5 changes: 2 additions & 3 deletions cashu/mint/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,6 @@ async def melt_quote(
unit=quote.unit,
request=quote.request,
fee_reserve=quote.fee_reserve,
paid=quote.paid, # deprecated
state=quote.state.value,
expiry=quote.expiry,
)
Expand Down Expand Up @@ -946,7 +945,7 @@ async def melt_mint_settle_internally(
return melt_quote

# we settle the transaction internally
if melt_quote.paid:
if melt_quote.state == MeltQuoteState.paid:
raise TransactionError("melt quote already paid")

# verify amounts from bolt11 invoice
Expand Down Expand Up @@ -1102,7 +1101,7 @@ async def melt(
# if the melt corresponds to an internal mint, mark both as paid
melt_quote = await self.melt_mint_settle_internally(melt_quote, proofs)
# quote not paid yet (not internal), pay it with the backend
if not melt_quote.paid:
if melt_quote.state == MeltQuoteState.pending:
logger.debug(f"Lightning: pay invoice {melt_quote.request}")
try:
payment = await self.backends[method][unit].pay_invoice(
Expand Down
45 changes: 45 additions & 0 deletions cashu/mint/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1261,3 +1261,48 @@ async def m035_add_last_checked_to_mint_quotes(db: Database):
ADD COLUMN last_checked TIMESTAMP NULL
"""
)


async def m036_remove_paid_from_melt_quote(db: Database):
"""Remove the deprecated 'paid' field from melt_quotes.
The 'state' column now fully represents payment status."""
async with db.connect() as conn:
if conn.type == "SQLITE":
await conn.execute("PRAGMA foreign_keys=OFF;")
await conn.execute(
f"""
CREATE TABLE {db.table_with_schema('melt_quotes_new')} (
quote TEXT NOT NULL,
method TEXT NOT NULL,
request TEXT NOT NULL,
checking_id TEXT NOT NULL,
unit TEXT NOT NULL,
amount {db.big_int} NOT NULL,
fee_reserve {db.big_int},
created_time TIMESTAMP,
paid_time TIMESTAMP,
fee_paid {db.big_int},
proof TEXT,
state TEXT,
expiry TIMESTAMP,
UNIQUE (quote)
);
"""
)
await conn.execute(
f"""
INSERT INTO {db.table_with_schema('melt_quotes_new')}
(quote, method, request, checking_id, unit, amount, fee_reserve, created_time, paid_time, fee_paid, proof, state, expiry)
SELECT quote, method, request, checking_id, unit, amount, fee_reserve, created_time, paid_time, fee_paid, proof, state, expiry
FROM {db.table_with_schema('melt_quotes')};
"""
)
await conn.execute(f"DROP TABLE {db.table_with_schema('melt_quotes')};")
await conn.execute(
f"ALTER TABLE {db.table_with_schema('melt_quotes_new')} RENAME TO {db.table_with_schema('melt_quotes')};"
)
await conn.execute("PRAGMA foreign_keys=ON;")
Comment thread
KvngMikey marked this conversation as resolved.
elif conn.type == "POSTGRES":
await conn.execute(
f"ALTER TABLE {db.table_with_schema('melt_quotes')} DROP COLUMN IF EXISTS paid;"
)
1 change: 0 additions & 1 deletion cashu/mint/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ async def get_melt_quote(request: Request, quote: str) -> PostMeltQuoteResponse:
unit=melt_quote.unit,
request=melt_quote.request,
fee_reserve=melt_quote.fee_reserve,
paid=melt_quote.paid,
state=melt_quote.state.value,
expiry=melt_quote.expiry,
payment_preimage=melt_quote.payment_preimage,
Expand Down
1 change: 0 additions & 1 deletion cashu/wallet/v1_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,6 @@ def _meltrequest_include_fields(
unit="sat",
request="lnbc0",
fee_reserve=0,
paid=ret.paid or False,
state=(
MeltQuoteState.paid.value
if ret.paid
Expand Down
18 changes: 9 additions & 9 deletions tests/fuzz/test_fuzz_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,17 @@ def test_fuzz_melt_quote(quote, method, request, checking_id, unit, amount, fee_

# Test property accessors
if state == MeltQuoteState.paid:
assert mq.paid
assert not mq.unpaid
assert not mq.pending
assert mq.state == MeltQuoteState.paid
assert mq.state != MeltQuoteState.unpaid
assert mq.state != MeltQuoteState.pending
elif state == MeltQuoteState.unpaid:
assert not mq.paid
assert mq.unpaid
assert not mq.pending
assert mq.state != MeltQuoteState.paid
assert mq.state == MeltQuoteState.unpaid
assert mq.state != MeltQuoteState.pending
elif state == MeltQuoteState.pending:
assert not mq.paid
assert not mq.unpaid
assert mq.pending
assert mq.state != MeltQuoteState.paid
assert mq.state != MeltQuoteState.unpaid
assert mq.state == MeltQuoteState.pending

@given(
quote=st.text(min_size=1, max_size=50),
Expand Down
20 changes: 0 additions & 20 deletions tests/mint/test_mint_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,26 +362,6 @@ async def test_melt_quote_internal(ledger: Ledger, wallet: Wallet):

assert result["expiry"] == expiry

# # get melt quote again from api
# response = httpx.get(
# f"{BASE_URL}/v1/melt/quote/bolt11/{result['quote']}",
# )
# assert response.status_code == 200, f"{response.url} {response.status_code}"
# result2 = response.json()
# assert result2["quote"] == result["quote"]

# # deserialize the response
# resp_quote = PostMeltQuoteResponse(**result2)
# assert resp_quote.quote == result["quote"]
# assert resp_quote.payment_preimage is not None
# assert len(resp_quote.payment_preimage) == 64
# assert resp_quote.change is not None
# assert resp_quote.state == MeltQuoteState.paid.value

# # check if DEPRECATED paid flag is also returned
# assert result2["paid"] is True
# assert resp_quote.paid is True


@pytest.mark.asyncio
@pytest.mark.skipif(
Expand Down
Loading