mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-21 02:54:20 +01:00
Mint: settle mint-melt on same mint with different units externally (#651)
* WIP settle different units externally * mint melt externally different units * deprecated route return only sat * comment --------- Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,7 @@ from .base import (
|
|||||||
|
|
||||||
|
|
||||||
class FakeWallet(LightningBackend):
|
class FakeWallet(LightningBackend):
|
||||||
|
unit: Unit
|
||||||
fake_btc_price = 1e8 / 1337
|
fake_btc_price = 1e8 / 1337
|
||||||
paid_invoices_queue: asyncio.Queue[Bolt11] = asyncio.Queue(0)
|
paid_invoices_queue: asyncio.Queue[Bolt11] = asyncio.Queue(0)
|
||||||
payment_secrets: Dict[str, str] = dict()
|
payment_secrets: Dict[str, str] = dict()
|
||||||
@@ -46,7 +47,6 @@ class FakeWallet(LightningBackend):
|
|||||||
).hex()
|
).hex()
|
||||||
|
|
||||||
supported_units = {Unit.sat, Unit.msat, Unit.usd, Unit.eur}
|
supported_units = {Unit.sat, Unit.msat, Unit.usd, Unit.eur}
|
||||||
unit = Unit.sat
|
|
||||||
|
|
||||||
supports_incoming_payment_stream: bool = True
|
supports_incoming_payment_stream: bool = True
|
||||||
supports_description: bool = True
|
supports_description: bool = True
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
# so that we would be able to handle the transaction internally
|
# so that we would be able to handle the transaction internally
|
||||||
# and therefore respond with internal transaction fees (0 for now)
|
# and therefore respond with internal transaction fees (0 for now)
|
||||||
mint_quote = await self.crud.get_mint_quote(request=request, db=self.db)
|
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)
|
payment_quote = self.create_internal_melt_quote(mint_quote, melt_quote)
|
||||||
else:
|
else:
|
||||||
# not internal
|
# not internal
|
||||||
@@ -811,6 +811,10 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
if not mint_quote:
|
if not mint_quote:
|
||||||
return melt_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
|
# 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")
|
||||||
@@ -825,8 +829,6 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
|
|||||||
raise TransactionError("amounts do not match")
|
raise TransactionError("amounts do not match")
|
||||||
if not bolt11_request == mint_quote.request:
|
if not bolt11_request == mint_quote.request:
|
||||||
raise TransactionError("bolt11 requests do not match")
|
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:
|
if not mint_quote.method == melt_quote.method:
|
||||||
raise TransactionError("methods do not match")
|
raise TransactionError("methods do not match")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
from ..core.base import BlindedMessage, BlindedSignature, Unit
|
||||||
from ..core.errors import CashuError
|
from ..core.errors import CashuError
|
||||||
from ..core.models import (
|
from ..core.models import (
|
||||||
CheckFeesRequest_deprecated,
|
CheckFeesRequest_deprecated,
|
||||||
@@ -114,7 +114,8 @@ async def keyset_deprecated(idBase64Urlsafe: str) -> Dict[str, str]:
|
|||||||
async def keysets_deprecated() -> KeysetsResponse_deprecated:
|
async def keysets_deprecated() -> KeysetsResponse_deprecated:
|
||||||
"""This endpoint returns a list of keysets that the mint currently supports and will accept tokens from."""
|
"""This endpoint returns a list of keysets that the mint currently supports and will accept tokens from."""
|
||||||
logger.trace("> GET /keysets")
|
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
|
return keysets
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ settings.mint_url = SERVER_ENDPOINT
|
|||||||
settings.tor = False
|
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.mint_backend_bolt11_usd = settings.mint_backend_bolt11_usd or "FakeWallet"
|
||||||
settings.fakewallet_brr = True
|
settings.fakewallet_brr = True
|
||||||
settings.fakewallet_delay_outgoing_payment = 0
|
settings.fakewallet_delay_outgoing_payment = 0
|
||||||
settings.fakewallet_delay_incoming_payment = 1
|
settings.fakewallet_delay_incoming_payment = 1
|
||||||
@@ -42,7 +43,7 @@ assert (
|
|||||||
), "Test database is the same as the main database"
|
), "Test database is the same as the main database"
|
||||||
settings.mint_database = settings.mint_test_database
|
settings.mint_database = settings.mint_test_database
|
||||||
settings.mint_derivation_path = "m/0'/0'/0'"
|
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_private_key = "TEST_PRIVATE_KEY"
|
||||||
settings.mint_seed_decryption_key = ""
|
settings.mint_seed_decryption_key = ""
|
||||||
settings.mint_max_balance = 0
|
settings.mint_max_balance = 0
|
||||||
@@ -85,13 +86,6 @@ class UvicornServer(multiprocessing.Process):
|
|||||||
async def ledger():
|
async def ledger():
|
||||||
async def start_mint_init(ledger: Ledger) -> Ledger:
|
async def start_mint_init(ledger: Ledger) -> Ledger:
|
||||||
await migrate_databases(ledger.db, migrations_mint)
|
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()
|
await ledger.startup_ledger()
|
||||||
return ledger
|
return ledger
|
||||||
|
|
||||||
@@ -110,9 +104,17 @@ async def ledger():
|
|||||||
await db.engine.dispose()
|
await db.engine.dispose()
|
||||||
|
|
||||||
wallets_module = importlib.import_module("cashu.lightning")
|
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 = {
|
backends = {
|
||||||
Method.bolt11: {Unit.sat: lightning_backend},
|
Method.bolt11: {
|
||||||
|
Unit.sat: lightning_backend_sat,
|
||||||
|
Unit.usd: lightning_backend_usd,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
ledger = Ledger(
|
ledger = Ledger(
|
||||||
db=Database("mint", settings.mint_database),
|
db=Database("mint", settings.mint_database),
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ async def test_api_keysets(ledger: Ledger):
|
|||||||
"active": True,
|
"active": True,
|
||||||
"input_fee_ppk": 0,
|
"input_fee_ppk": 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "00c074b96c7e2b0e",
|
||||||
|
"unit": "usd",
|
||||||
|
"active": True,
|
||||||
|
"input_fee_ppk": 0,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
assert response.json() == expected
|
assert response.json() == expected
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import httpx
|
|||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
from cashu.core.base import Proof
|
from cashu.core.base import Proof, Unit
|
||||||
from cashu.core.models import (
|
from cashu.core.models import (
|
||||||
CheckSpendableRequest_deprecated,
|
CheckSpendableRequest_deprecated,
|
||||||
CheckSpendableResponse_deprecated,
|
CheckSpendableResponse_deprecated,
|
||||||
@@ -51,7 +51,8 @@ async def test_api_keysets(ledger: Ledger):
|
|||||||
response = httpx.get(f"{BASE_URL}/keysets")
|
response = httpx.get(f"{BASE_URL}/keysets")
|
||||||
assert response.status_code == 200, f"{response.url} {response.status_code}"
|
assert response.status_code == 200, f"{response.url} {response.status_code}"
|
||||||
assert ledger.keyset.public_keys
|
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
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ async def wallet(ledger: Ledger):
|
|||||||
async def test_init_keysets(ledger: Ledger):
|
async def test_init_keysets(ledger: Ledger):
|
||||||
ledger.keysets = {}
|
ledger.keysets = {}
|
||||||
await ledger.init_keysets()
|
await ledger.init_keysets()
|
||||||
assert len(ledger.keysets) == 1
|
assert len(ledger.keysets) == 2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import pytest_asyncio
|
|||||||
|
|
||||||
from cashu.core.base import MeltQuote, MeltQuoteState, Proof
|
from cashu.core.base import MeltQuote, MeltQuoteState, Proof
|
||||||
from cashu.core.errors import LightningError
|
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.core.settings import settings
|
||||||
from cashu.lightning.base import PaymentResult
|
from cashu.lightning.base import PaymentResult
|
||||||
from cashu.mint.ledger import Ledger
|
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),
|
ledger.melt(proofs=wallet.proofs, quote=quote_id),
|
||||||
"Melt is disabled. Please contact the operator.",
|
"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)
|
||||||
|
|||||||
Reference in New Issue
Block a user