diff --git a/cashu/lightning/fake.py b/cashu/lightning/fake.py index 59073fd1..1d3cbab1 100644 --- a/cashu/lightning/fake.py +++ b/cashu/lightning/fake.py @@ -30,6 +30,7 @@ class FakeWallet(LightningBackend): + unit: Unit fake_btc_price = 1e8 / 1337 paid_invoices_queue: asyncio.Queue[Bolt11] = asyncio.Queue(0) payment_secrets: Dict[str, str] = dict() @@ -46,7 +47,6 @@ class FakeWallet(LightningBackend): ).hex() supported_units = {Unit.sat, Unit.msat, Unit.usd, Unit.eur} - unit = Unit.sat supports_incoming_payment_stream: bool = True supports_description: bool = True diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index c08531e2..8dae66c7 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -660,7 +660,7 @@ async def melt_quote( # so that we would be able to handle the transaction internally # and therefore respond with internal transaction fees (0 for now) mint_quote = await self.crud.get_mint_quote(request=request, db=self.db) - if mint_quote: + if mint_quote and mint_quote.unit == melt_quote.unit: payment_quote = self.create_internal_melt_quote(mint_quote, melt_quote) else: # not internal @@ -811,6 +811,10 @@ async def melt_mint_settle_internally( if not mint_quote: return melt_quote + # settle externally if units are different + if mint_quote.unit != melt_quote.unit: + return melt_quote + # we settle the transaction internally if melt_quote.paid: raise TransactionError("melt quote already paid") @@ -825,8 +829,6 @@ async def melt_mint_settle_internally( raise TransactionError("amounts do not match") if not bolt11_request == mint_quote.request: raise TransactionError("bolt11 requests do not match") - if not mint_quote.unit == melt_quote.unit: - raise TransactionError("units do not match") if not mint_quote.method == melt_quote.method: raise TransactionError("methods do not match") diff --git a/cashu/mint/router_deprecated.py b/cashu/mint/router_deprecated.py index d09c2f72..73f39d91 100644 --- a/cashu/mint/router_deprecated.py +++ b/cashu/mint/router_deprecated.py @@ -3,7 +3,7 @@ from fastapi import APIRouter, Request from loguru import logger -from ..core.base import BlindedMessage, BlindedSignature +from ..core.base import BlindedMessage, BlindedSignature, Unit from ..core.errors import CashuError from ..core.models import ( CheckFeesRequest_deprecated, @@ -114,7 +114,8 @@ async def keyset_deprecated(idBase64Urlsafe: str) -> Dict[str, str]: async def keysets_deprecated() -> KeysetsResponse_deprecated: """This endpoint returns a list of keysets that the mint currently supports and will accept tokens from.""" logger.trace("> GET /keysets") - keysets = KeysetsResponse_deprecated(keysets=list(ledger.keysets.keys())) + sat_keysets = {k: v for k, v in ledger.keysets.items() if v.unit == Unit.sat} + keysets = KeysetsResponse_deprecated(keysets=list(sat_keysets.keys())) return keysets diff --git a/tests/conftest.py b/tests/conftest.py index 024f02b7..4ccebc61 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,6 +33,7 @@ settings.tor = False settings.wallet_unit = "sat" settings.mint_backend_bolt11_sat = settings.mint_backend_bolt11_sat or "FakeWallet" +settings.mint_backend_bolt11_usd = settings.mint_backend_bolt11_usd or "FakeWallet" settings.fakewallet_brr = True settings.fakewallet_delay_outgoing_payment = 0 settings.fakewallet_delay_incoming_payment = 1 @@ -42,7 +43,7 @@ ), "Test database is the same as the main database" settings.mint_database = settings.mint_test_database settings.mint_derivation_path = "m/0'/0'/0'" -settings.mint_derivation_path_list = [] +settings.mint_derivation_path_list = ["m/0'/2'/0'"] # USD settings.mint_private_key = "TEST_PRIVATE_KEY" settings.mint_seed_decryption_key = "" settings.mint_max_balance = 0 @@ -85,13 +86,6 @@ def run(self, *args, **kwargs): async def ledger(): async def start_mint_init(ledger: Ledger) -> Ledger: await migrate_databases(ledger.db, migrations_mint) - ledger = Ledger( - db=Database("mint", settings.mint_database), - seed=settings.mint_private_key, - derivation_path=settings.mint_derivation_path, - backends=backends, - crud=LedgerCrudSqlite(), - ) await ledger.startup_ledger() return ledger @@ -110,9 +104,17 @@ async def start_mint_init(ledger: Ledger) -> Ledger: await db.engine.dispose() wallets_module = importlib.import_module("cashu.lightning") - lightning_backend = getattr(wallets_module, settings.mint_backend_bolt11_sat)() + lightning_backend_sat = getattr(wallets_module, settings.mint_backend_bolt11_sat)( + unit=Unit.sat + ) + lightning_backend_usd = getattr(wallets_module, settings.mint_backend_bolt11_usd)( + unit=Unit.usd + ) backends = { - Method.bolt11: {Unit.sat: lightning_backend}, + Method.bolt11: { + Unit.sat: lightning_backend_sat, + Unit.usd: lightning_backend_usd, + }, } ledger = Ledger( db=Database("mint", settings.mint_database), diff --git a/tests/test_mint_api.py b/tests/test_mint_api.py index aa5b402f..fe38bc70 100644 --- a/tests/test_mint_api.py +++ b/tests/test_mint_api.py @@ -94,6 +94,12 @@ async def test_api_keysets(ledger: Ledger): "active": True, "input_fee_ppk": 0, }, + { + "id": "00c074b96c7e2b0e", + "unit": "usd", + "active": True, + "input_fee_ppk": 0, + }, ] } assert response.json() == expected diff --git a/tests/test_mint_api_deprecated.py b/tests/test_mint_api_deprecated.py index b9aac81a..226af28a 100644 --- a/tests/test_mint_api_deprecated.py +++ b/tests/test_mint_api_deprecated.py @@ -2,7 +2,7 @@ import pytest import pytest_asyncio -from cashu.core.base import Proof +from cashu.core.base import Proof, Unit from cashu.core.models import ( CheckSpendableRequest_deprecated, CheckSpendableResponse_deprecated, @@ -51,7 +51,8 @@ async def test_api_keysets(ledger: Ledger): response = httpx.get(f"{BASE_URL}/keysets") assert response.status_code == 200, f"{response.url} {response.status_code}" assert ledger.keyset.public_keys - assert response.json()["keysets"] == list(ledger.keysets.keys()) + sat_keysets = {k: v for k, v in ledger.keysets.items() if v.unit == Unit.sat} + assert response.json()["keysets"] == list(sat_keysets.keys()) @pytest.mark.asyncio diff --git a/tests/test_mint_init.py b/tests/test_mint_init.py index 71aa7021..60817fe3 100644 --- a/tests/test_mint_init.py +++ b/tests/test_mint_init.py @@ -60,7 +60,7 @@ async def wallet(ledger: Ledger): async def test_init_keysets(ledger: Ledger): ledger.keysets = {} await ledger.init_keysets() - assert len(ledger.keysets) == 1 + assert len(ledger.keysets) == 2 @pytest.mark.asyncio diff --git a/tests/test_mint_melt.py b/tests/test_mint_melt.py index 8d2d2489..c135992f 100644 --- a/tests/test_mint_melt.py +++ b/tests/test_mint_melt.py @@ -5,7 +5,7 @@ from cashu.core.base import MeltQuote, MeltQuoteState, Proof from cashu.core.errors import LightningError -from cashu.core.models import PostMeltQuoteRequest +from cashu.core.models import PostMeltQuoteRequest, PostMintQuoteRequest from cashu.core.settings import settings from cashu.lightning.base import PaymentResult from cashu.mint.ledger import Ledger @@ -331,3 +331,40 @@ async def test_melt_lightning_pay_invoice_exception_exception( ledger.melt(proofs=wallet.proofs, quote=quote_id), "Melt is disabled. Please contact the operator.", ) + + +@pytest.mark.asyncio +@pytest.mark.skipif(is_regtest, reason="only fake wallet") +async def test_mint_melt_different_units(ledger: Ledger, wallet: Wallet): + """Mint and melt different units.""" + # load the wallet + invoice = await wallet.request_mint(64) + await wallet.mint(64, id=invoice.id) + + amount = 32 + + # mint quote in sat + sat_mint_quote = await ledger.mint_quote( + quote_request=PostMintQuoteRequest(amount=amount, unit="sat") + ) + sat_invoice = sat_mint_quote.request + assert sat_mint_quote.paid is False + + # melt quote in usd + usd_melt_quote = await ledger.melt_quote( + PostMeltQuoteRequest(unit="usd", request=sat_invoice) + ) + assert usd_melt_quote.paid is False + + # pay melt quote with usd + await ledger.melt(proofs=wallet.proofs, quote=usd_melt_quote.quote) + + output_amounts = [32] + + secrets, rs, derivation_paths = await wallet.generate_n_secrets(len(output_amounts)) + outputs, rs = wallet._construct_outputs(output_amounts, secrets, rs) + + # mint in sat + mint_resp = await ledger.mint(outputs=outputs, quote_id=sat_mint_quote.quote) + + assert len(mint_resp) == len(outputs)