mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-02-23 09:34:22 +01:00
Add fees (#503)
* wip * wip * model * refactor wallet transactions * refactor wallet * sending with fees works and outputs fill up the wallet * wip work * ok * comments * receive with amount=0 * correctly import postmeltrequest * fix melt amount * tests working * remove mint_loaded decorator in deprecated wallet api * wallet works with units * refactor: melt_quote * fix fees * add file * fees for melt inputs * set default input fee for internal quotes to 0 * fix coinselect * coin selection working * yo * fix all tests * clean up * last commit added fees for inputs for melt transactions - this commit adds a blanace too low exception * fix fee return and melt quote max allowed amount check during creation of melt quote * clean up code * add tests for fees * add melt tests * update wallet fee information
This commit is contained in:
@@ -45,6 +45,7 @@ settings.mint_private_key = "TEST_PRIVATE_KEY"
|
||||
settings.mint_seed_decryption_key = ""
|
||||
settings.mint_max_balance = 0
|
||||
settings.mint_lnd_enable_mpp = True
|
||||
settings.mint_input_fee_ppk = 0
|
||||
|
||||
assert "test" in settings.cashu_dir
|
||||
shutil.rmtree(settings.cashu_dir, ignore_errors=True)
|
||||
|
||||
@@ -2,9 +2,10 @@ from typing import List
|
||||
|
||||
import pytest
|
||||
|
||||
from cashu.core.base import BlindedMessage, PostMintQuoteRequest, Proof
|
||||
from cashu.core.base import BlindedMessage, Proof
|
||||
from cashu.core.crypto.b_dhke import step1_alice
|
||||
from cashu.core.helpers import calculate_number_of_blank_outputs
|
||||
from cashu.core.models import PostMintQuoteRequest
|
||||
from cashu.core.settings import settings
|
||||
from cashu.mint.ledger import Ledger
|
||||
from tests.helpers import pay_if_regtest
|
||||
@@ -129,9 +130,9 @@ async def test_generate_promises(ledger: Ledger):
|
||||
async def test_generate_change_promises(ledger: Ledger):
|
||||
# Example slightly adapted from NUT-08 because we want to ensure the dynamic change
|
||||
# token amount works: `n_blank_outputs != n_returned_promises != 4`.
|
||||
invoice_amount = 100_000
|
||||
# invoice_amount = 100_000
|
||||
fee_reserve = 2_000
|
||||
total_provided = invoice_amount + fee_reserve
|
||||
# total_provided = invoice_amount + fee_reserve
|
||||
actual_fee = 100
|
||||
|
||||
expected_returned_promises = 7 # Amounts = [4, 8, 32, 64, 256, 512, 1024]
|
||||
@@ -149,7 +150,7 @@ async def test_generate_change_promises(ledger: Ledger):
|
||||
]
|
||||
|
||||
promises = await ledger._generate_change_promises(
|
||||
total_provided, invoice_amount, actual_fee, outputs
|
||||
fee_provided=fee_reserve, fee_paid=actual_fee, outputs=outputs
|
||||
)
|
||||
|
||||
assert len(promises) == expected_returned_promises
|
||||
@@ -160,9 +161,9 @@ async def test_generate_change_promises(ledger: Ledger):
|
||||
async def test_generate_change_promises_legacy_wallet(ledger: Ledger):
|
||||
# Check if mint handles a legacy wallet implementation (always sends 4 blank
|
||||
# outputs) as well.
|
||||
invoice_amount = 100_000
|
||||
# invoice_amount = 100_000
|
||||
fee_reserve = 2_000
|
||||
total_provided = invoice_amount + fee_reserve
|
||||
# total_provided = invoice_amount + fee_reserve
|
||||
actual_fee = 100
|
||||
|
||||
expected_returned_promises = 4 # Amounts = [64, 256, 512, 1024]
|
||||
@@ -179,9 +180,7 @@ async def test_generate_change_promises_legacy_wallet(ledger: Ledger):
|
||||
for b, _ in blinded_msgs
|
||||
]
|
||||
|
||||
promises = await ledger._generate_change_promises(
|
||||
total_provided, invoice_amount, actual_fee, outputs
|
||||
)
|
||||
promises = await ledger._generate_change_promises(fee_reserve, actual_fee, outputs)
|
||||
|
||||
assert len(promises) == expected_returned_promises
|
||||
assert sum([promise.amount for promise in promises]) == expected_returned_fees
|
||||
@@ -189,14 +188,14 @@ async def test_generate_change_promises_legacy_wallet(ledger: Ledger):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_change_promises_returns_empty_if_no_outputs(ledger: Ledger):
|
||||
invoice_amount = 100_000
|
||||
# invoice_amount = 100_000
|
||||
fee_reserve = 1_000
|
||||
total_provided = invoice_amount + fee_reserve
|
||||
# total_provided = invoice_amount + fee_reserve
|
||||
actual_fee_msat = 100_000
|
||||
outputs = None
|
||||
|
||||
promises = await ledger._generate_change_promises(
|
||||
total_provided, invoice_amount, actual_fee_msat, outputs
|
||||
fee_reserve, actual_fee_msat, outputs
|
||||
)
|
||||
assert len(promises) == 0
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ import httpx
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.base import (
|
||||
from cashu.core.base import SpentState
|
||||
from cashu.core.models import (
|
||||
GetInfoResponse,
|
||||
MintMeltMethodSetting,
|
||||
PostCheckStateRequest,
|
||||
PostCheckStateResponse,
|
||||
PostRestoreRequest,
|
||||
PostRestoreResponse,
|
||||
SpentState,
|
||||
)
|
||||
from cashu.core.settings import settings
|
||||
from cashu.mint.ledger import Ledger
|
||||
@@ -89,6 +89,7 @@ async def test_api_keysets(ledger: Ledger):
|
||||
"id": "009a1f293253e41e",
|
||||
"unit": "sat",
|
||||
"active": True,
|
||||
"input_fee_ppk": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import httpx
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.base import (
|
||||
from cashu.core.base import Proof
|
||||
from cashu.core.models import (
|
||||
CheckSpendableRequest_deprecated,
|
||||
CheckSpendableResponse_deprecated,
|
||||
PostRestoreRequest,
|
||||
PostRestoreResponse,
|
||||
Proof,
|
||||
)
|
||||
from cashu.mint.ledger import Ledger
|
||||
from cashu.wallet.crud import bump_secret_derivation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.base import PostMeltQuoteRequest
|
||||
from cashu.core.models import PostMeltQuoteRequest
|
||||
from cashu.mint.ledger import Ledger
|
||||
from cashu.wallet.wallet import Wallet
|
||||
from cashu.wallet.wallet import Wallet as Wallet1
|
||||
|
||||
241
tests/test_mint_fees.py
Normal file
241
tests/test_mint_fees.py
Normal file
@@ -0,0 +1,241 @@
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.helpers import sum_proofs
|
||||
from cashu.core.models import PostMeltQuoteRequest
|
||||
from cashu.core.split import amount_split
|
||||
from cashu.mint.ledger import Ledger
|
||||
from cashu.wallet.wallet import Wallet
|
||||
from cashu.wallet.wallet import Wallet as Wallet1
|
||||
from tests.conftest import SERVER_ENDPOINT
|
||||
from tests.helpers import get_real_invoice, is_fake, is_regtest, pay_if_regtest
|
||||
|
||||
|
||||
async def assert_err(f, msg):
|
||||
"""Compute f() and expect an error message 'msg'."""
|
||||
try:
|
||||
await f
|
||||
except Exception as exc:
|
||||
if msg not in str(exc.args[0]):
|
||||
raise Exception(f"Expected error: {msg}, got: {exc.args[0]}")
|
||||
return
|
||||
raise Exception(f"Expected error: {msg}, got no error")
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def wallet1(ledger: Ledger):
|
||||
wallet1 = await Wallet1.with_db(
|
||||
url=SERVER_ENDPOINT,
|
||||
db="test_data/wallet1",
|
||||
name="wallet1",
|
||||
)
|
||||
await wallet1.load_mint()
|
||||
yield wallet1
|
||||
|
||||
|
||||
def set_ledger_keyset_fees(
|
||||
fee_ppk: int, ledger: Ledger, wallet: Optional[Wallet] = None
|
||||
):
|
||||
for keyset in ledger.keysets.values():
|
||||
keyset.input_fee_ppk = fee_ppk
|
||||
|
||||
if wallet:
|
||||
for wallet_keyset in wallet.keysets.values():
|
||||
wallet_keyset.input_fee_ppk = fee_ppk
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_fees_for_proofs(wallet1: Wallet, ledger: Ledger):
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, split=[1] * 64, id=invoice.id)
|
||||
|
||||
# two proofs
|
||||
|
||||
set_ledger_keyset_fees(100, ledger)
|
||||
proofs = [wallet1.proofs[0], wallet1.proofs[1]]
|
||||
fees = ledger.get_fees_for_proofs(proofs)
|
||||
assert fees == 1
|
||||
|
||||
set_ledger_keyset_fees(1234, ledger)
|
||||
fees = ledger.get_fees_for_proofs(proofs)
|
||||
assert fees == 3
|
||||
|
||||
set_ledger_keyset_fees(0, ledger)
|
||||
fees = ledger.get_fees_for_proofs(proofs)
|
||||
assert fees == 0
|
||||
|
||||
set_ledger_keyset_fees(1, ledger)
|
||||
fees = ledger.get_fees_for_proofs(proofs)
|
||||
assert fees == 1
|
||||
|
||||
# ten proofs
|
||||
|
||||
ten_proofs = wallet1.proofs[:10]
|
||||
set_ledger_keyset_fees(100, ledger)
|
||||
fees = ledger.get_fees_for_proofs(ten_proofs)
|
||||
assert fees == 1
|
||||
|
||||
set_ledger_keyset_fees(101, ledger)
|
||||
fees = ledger.get_fees_for_proofs(ten_proofs)
|
||||
assert fees == 2
|
||||
|
||||
# three proofs
|
||||
|
||||
three_proofs = wallet1.proofs[:3]
|
||||
set_ledger_keyset_fees(333, ledger)
|
||||
fees = ledger.get_fees_for_proofs(three_proofs)
|
||||
assert fees == 1
|
||||
|
||||
set_ledger_keyset_fees(334, ledger)
|
||||
fees = ledger.get_fees_for_proofs(three_proofs)
|
||||
assert fees == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skipif_with_fees(is_regtest, reason="only works with FakeWallet")
|
||||
async def test_wallet_fee(wallet1: Wallet, ledger: Ledger):
|
||||
# THIS TEST IS A FAKE, WE SET THE WALLET FEES MANUALLY IN set_ledger_keyset_fees
|
||||
# It would be better to test if the wallet can get the fees from the mint itself
|
||||
# but the ledger instance does not update the responses from the `mint` that is running in the background
|
||||
# so we just pretend here and test really nothing...
|
||||
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(100, ledger, wallet1)
|
||||
|
||||
# check if all wallet keysets have the correct fees
|
||||
for keyset in wallet1.keysets.values():
|
||||
assert keyset.input_fee_ppk == 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_split_with_fees(wallet1: Wallet, ledger: Ledger):
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(100, ledger)
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
|
||||
send_proofs, _ = await wallet1.select_to_send(wallet1.proofs, 10)
|
||||
fees = ledger.get_fees_for_proofs(send_proofs)
|
||||
assert fees == 1
|
||||
outputs = await wallet1.construct_outputs(amount_split(9))
|
||||
|
||||
promises = await ledger.split(proofs=send_proofs, outputs=outputs)
|
||||
assert len(promises) == len(outputs)
|
||||
assert [p.amount for p in promises] == [p.amount for p in outputs]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_split_with_high_fees(wallet1: Wallet, ledger: Ledger):
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(1234, ledger)
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
|
||||
send_proofs, _ = await wallet1.select_to_send(wallet1.proofs, 10)
|
||||
fees = ledger.get_fees_for_proofs(send_proofs)
|
||||
assert fees == 3
|
||||
outputs = await wallet1.construct_outputs(amount_split(7))
|
||||
|
||||
promises = await ledger.split(proofs=send_proofs, outputs=outputs)
|
||||
assert len(promises) == len(outputs)
|
||||
assert [p.amount for p in promises] == [p.amount for p in outputs]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_split_not_enough_fees(wallet1: Wallet, ledger: Ledger):
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(100, ledger)
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
|
||||
send_proofs, _ = await wallet1.select_to_send(wallet1.proofs, 10)
|
||||
fees = ledger.get_fees_for_proofs(send_proofs)
|
||||
assert fees == 1
|
||||
# with 10 sat input, we request 10 sat outputs but fees are 1 sat so the swap will fail
|
||||
outputs = await wallet1.construct_outputs(amount_split(10))
|
||||
|
||||
await assert_err(
|
||||
ledger.split(proofs=send_proofs, outputs=outputs), "are not balanced"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skipif(is_regtest, reason="only works with FakeWallet")
|
||||
async def test_melt_internal(wallet1: Wallet, ledger: Ledger):
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(100, ledger, wallet1)
|
||||
|
||||
# mint twice so we have enough to pay the second invoice back
|
||||
invoice = await wallet1.request_mint(128)
|
||||
await wallet1.mint(128, id=invoice.id)
|
||||
assert wallet1.balance == 128
|
||||
|
||||
# create a mint quote so that we can melt to it internally
|
||||
invoice_to_pay = await wallet1.request_mint(64)
|
||||
invoice_payment_request = invoice_to_pay.bolt11
|
||||
|
||||
melt_quote = await ledger.melt_quote(
|
||||
PostMeltQuoteRequest(request=invoice_payment_request, unit="sat")
|
||||
)
|
||||
assert not melt_quote.paid
|
||||
assert melt_quote.amount == 64
|
||||
assert melt_quote.fee_reserve == 0
|
||||
|
||||
melt_quote_pre_payment = await ledger.get_melt_quote(melt_quote.quote)
|
||||
assert not melt_quote_pre_payment.paid, "melt quote should not be paid"
|
||||
|
||||
# let's first try to melt without enough funds
|
||||
send_proofs, fees = await wallet1.select_to_send(wallet1.proofs, 63)
|
||||
# this should fail because we need 64 + 1 sat fees
|
||||
assert sum_proofs(send_proofs) == 64
|
||||
await assert_err(
|
||||
ledger.melt(proofs=send_proofs, quote=melt_quote.quote),
|
||||
"not enough inputs provided for melt",
|
||||
)
|
||||
|
||||
# the wallet respects the fees for coin selection
|
||||
send_proofs, fees = await wallet1.select_to_send(wallet1.proofs, 64)
|
||||
# includes 1 sat fees
|
||||
assert sum_proofs(send_proofs) == 65
|
||||
await ledger.melt(proofs=send_proofs, quote=melt_quote.quote)
|
||||
|
||||
melt_quote_post_payment = await ledger.get_melt_quote(melt_quote.quote)
|
||||
assert melt_quote_post_payment.paid, "melt quote should be paid"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skipif(is_fake, reason="only works with Regtest")
|
||||
async def test_melt_external_with_fees(wallet1: Wallet, ledger: Ledger):
|
||||
# set fees to 100 ppk
|
||||
set_ledger_keyset_fees(100, ledger, wallet1)
|
||||
|
||||
# mint twice so we have enough to pay the second invoice back
|
||||
invoice = await wallet1.request_mint(128)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(128, id=invoice.id)
|
||||
assert wallet1.balance == 128
|
||||
|
||||
invoice_dict = get_real_invoice(64)
|
||||
invoice_payment_request = invoice_dict["payment_request"]
|
||||
|
||||
mint_quote = await wallet1.melt_quote(invoice_payment_request)
|
||||
total_amount = mint_quote.amount + mint_quote.fee_reserve
|
||||
send_proofs, fee = await wallet1.select_to_send(wallet1.proofs, total_amount)
|
||||
melt_quote = await ledger.melt_quote(
|
||||
PostMeltQuoteRequest(request=invoice_payment_request, unit="sat")
|
||||
)
|
||||
|
||||
melt_quote_pre_payment = await ledger.get_melt_quote(melt_quote.quote)
|
||||
assert not melt_quote_pre_payment.paid, "melt quote should not be paid"
|
||||
|
||||
assert not melt_quote.paid, "melt quote should not be paid"
|
||||
await ledger.melt(proofs=send_proofs, quote=melt_quote.quote)
|
||||
|
||||
melt_quote_post_payment = await ledger.get_melt_quote(melt_quote.quote)
|
||||
assert melt_quote_post_payment.paid, "melt quote should be paid"
|
||||
@@ -2,7 +2,8 @@ import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
from cashu.core.base import Amount, MeltQuote, PostMeltQuoteRequest, Unit
|
||||
from cashu.core.base import Amount, MeltQuote, Unit
|
||||
from cashu.core.models import PostMeltQuoteRequest
|
||||
from cashu.core.settings import settings
|
||||
from cashu.lightning.blink import MINIMUM_FEE_MSAT, BlinkWallet # type: ignore
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.base import PostMeltQuoteRequest, PostMintQuoteRequest
|
||||
from cashu.core.helpers import sum_proofs
|
||||
from cashu.core.models import PostMeltQuoteRequest, PostMintQuoteRequest
|
||||
from cashu.mint.ledger import Ledger
|
||||
from cashu.wallet.wallet import Wallet
|
||||
from cashu.wallet.wallet import Wallet as Wallet1
|
||||
@@ -155,6 +155,18 @@ async def test_split(wallet1: Wallet, ledger: Ledger):
|
||||
assert [p.amount for p in promises] == [p.amount for p in outputs]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_split_with_no_outputs(wallet1: Wallet, ledger: Ledger):
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10, set_reserved=False)
|
||||
await assert_err(
|
||||
ledger.split(proofs=send_proofs, outputs=[]),
|
||||
"no outputs provided",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_split_with_input_less_than_outputs(wallet1: Wallet, ledger: Ledger):
|
||||
invoice = await wallet1.request_mint(64)
|
||||
@@ -165,19 +177,19 @@ async def test_split_with_input_less_than_outputs(wallet1: Wallet, ledger: Ledge
|
||||
wallet1.proofs, 10, set_reserved=False
|
||||
)
|
||||
|
||||
all_send_proofs = send_proofs + keep_proofs
|
||||
too_many_proofs = send_proofs + send_proofs
|
||||
|
||||
# generate outputs for all proofs, not only the sent ones
|
||||
# generate more outputs than inputs
|
||||
secrets, rs, derivation_paths = await wallet1.generate_n_secrets(
|
||||
len(all_send_proofs)
|
||||
len(too_many_proofs)
|
||||
)
|
||||
outputs, rs = wallet1._construct_outputs(
|
||||
[p.amount for p in all_send_proofs], secrets, rs
|
||||
[p.amount for p in too_many_proofs], secrets, rs
|
||||
)
|
||||
|
||||
await assert_err(
|
||||
ledger.split(proofs=send_proofs, outputs=outputs),
|
||||
"inputs do not have same amount as outputs.",
|
||||
"are not balanced",
|
||||
)
|
||||
|
||||
# make sure we can still spend our tokens
|
||||
@@ -201,7 +213,7 @@ async def test_split_with_input_more_than_outputs(wallet1: Wallet, ledger: Ledge
|
||||
|
||||
await assert_err(
|
||||
ledger.split(proofs=inputs, outputs=outputs),
|
||||
"inputs do not have same amount as outputs",
|
||||
"are not balanced",
|
||||
)
|
||||
|
||||
# make sure we can still spend our tokens
|
||||
@@ -216,6 +228,9 @@ async def test_split_twice_with_same_outputs(wallet1: Wallet, ledger: Ledger):
|
||||
inputs1 = wallet1.proofs[:1]
|
||||
inputs2 = wallet1.proofs[1:]
|
||||
|
||||
assert inputs1[0].amount == 64
|
||||
assert inputs2[0].amount == 64
|
||||
|
||||
output_amounts = [64]
|
||||
secrets, rs, derivation_paths = await wallet1.generate_n_secrets(
|
||||
len(output_amounts)
|
||||
|
||||
@@ -42,14 +42,14 @@ async def assert_err(f, msg: Union[str, CashuError]):
|
||||
|
||||
def assert_amt(proofs: List[Proof], expected: int):
|
||||
"""Assert amounts the proofs contain."""
|
||||
assert [p.amount for p in proofs] == expected
|
||||
assert sum([p.amount for p in proofs]) == expected
|
||||
|
||||
|
||||
async def reset_wallet_db(wallet: Wallet):
|
||||
await wallet.db.execute("DELETE FROM proofs")
|
||||
await wallet.db.execute("DELETE FROM proofs_used")
|
||||
await wallet.db.execute("DELETE FROM keysets")
|
||||
await wallet._load_mint()
|
||||
await wallet.load_mint()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
@@ -97,7 +97,7 @@ async def test_get_keyset(wallet1: Wallet):
|
||||
# gets the keys of a specific keyset
|
||||
assert keyset.id is not None
|
||||
assert keyset.public_keys is not None
|
||||
keys2 = await wallet1._get_keys_of_keyset(keyset.id)
|
||||
keys2 = await wallet1._get_keyset(keyset.id)
|
||||
assert keys2.public_keys is not None
|
||||
assert len(keyset.public_keys) == len(keys2.public_keys)
|
||||
|
||||
@@ -105,12 +105,12 @@ async def test_get_keyset(wallet1: Wallet):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_keyset_from_db(wallet1: Wallet):
|
||||
# first load it from the mint
|
||||
# await wallet1._load_mint_keys()
|
||||
# await wallet1.activate_keyset()
|
||||
# NOTE: conftest already called wallet.load_mint() which got the keys from the mint
|
||||
keyset1 = copy.copy(wallet1.keysets[wallet1.keyset_id])
|
||||
|
||||
# then load it from the db
|
||||
await wallet1._load_mint_keys()
|
||||
await wallet1.activate_keyset()
|
||||
keyset2 = copy.copy(wallet1.keysets[wallet1.keyset_id])
|
||||
|
||||
assert keyset1.public_keys == keyset2.public_keys
|
||||
@@ -133,17 +133,17 @@ async def test_get_info(wallet1: Wallet):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_nonexistent_keyset(wallet1: Wallet):
|
||||
await assert_err(
|
||||
wallet1._get_keys_of_keyset("nonexistent"),
|
||||
wallet1._get_keyset("nonexistent"),
|
||||
KeysetNotFoundError(),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_keyset_ids(wallet1: Wallet):
|
||||
keysets = await wallet1._get_keyset_ids()
|
||||
async def test_get_keysets(wallet1: Wallet):
|
||||
keysets = await wallet1._get_keysets()
|
||||
assert isinstance(keysets, list)
|
||||
assert len(keysets) > 0
|
||||
assert wallet1.keyset_id in keysets
|
||||
assert wallet1.keyset_id in [k.id for k in keysets]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -156,6 +156,7 @@ async def test_request_mint(wallet1: Wallet):
|
||||
async def test_mint(wallet1: Wallet):
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
expected_proof_amounts = wallet1.split_wallet_state(64)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
assert wallet1.balance == 64
|
||||
|
||||
@@ -168,7 +169,8 @@ async def test_mint(wallet1: Wallet):
|
||||
proofs_minted = await get_proofs(
|
||||
db=wallet1.db, mint_id=invoice_db.id, table="proofs"
|
||||
)
|
||||
assert len(proofs_minted) == 1
|
||||
assert len(proofs_minted) == len(expected_proof_amounts)
|
||||
assert all([p.amount in expected_proof_amounts for p in proofs_minted])
|
||||
assert all([p.mint_id == invoice.id for p in proofs_minted])
|
||||
|
||||
|
||||
@@ -212,11 +214,15 @@ async def test_split(wallet1: Wallet):
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
assert wallet1.balance == 64
|
||||
# the outputs we keep that we expect after the split
|
||||
expected_proof_amounts = wallet1.split_wallet_state(44)
|
||||
p1, p2 = await wallet1.split(wallet1.proofs, 20)
|
||||
assert wallet1.balance == 64
|
||||
assert sum_proofs(p1) == 44
|
||||
assert [p.amount for p in p1] == [4, 8, 32]
|
||||
# what we keep should have the expected amounts
|
||||
assert [p.amount for p in p1] == expected_proof_amounts
|
||||
assert sum_proofs(p2) == 20
|
||||
# what we send should be the optimal split
|
||||
assert [p.amount for p in p2] == [4, 16]
|
||||
assert all([p.id == wallet1.keyset_id for p in p1])
|
||||
assert all([p.id == wallet1.keyset_id for p in p2])
|
||||
@@ -227,13 +233,19 @@ async def test_split_to_send(wallet1: Wallet):
|
||||
invoice = await wallet1.request_mint(64)
|
||||
pay_if_regtest(invoice.bolt11)
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
keep_proofs, spendable_proofs = await wallet1.split_to_send(
|
||||
assert wallet1.balance == 64
|
||||
|
||||
# this will select 32 sats and them (nothing to keep)
|
||||
keep_proofs, send_proofs = await wallet1.split_to_send(
|
||||
wallet1.proofs, 32, set_reserved=True
|
||||
)
|
||||
get_spendable = await wallet1._select_proofs_to_send(wallet1.proofs, 32)
|
||||
assert keep_proofs == get_spendable
|
||||
assert_amt(send_proofs, 32)
|
||||
assert_amt(keep_proofs, 0)
|
||||
|
||||
spendable_proofs = await wallet1._select_proofs_to_send(wallet1.proofs, 32)
|
||||
assert sum_proofs(spendable_proofs) == 32
|
||||
|
||||
assert sum_proofs(send_proofs) == 32
|
||||
assert wallet1.balance == 64
|
||||
assert wallet1.available_balance == 32
|
||||
|
||||
@@ -271,7 +283,7 @@ async def test_melt(wallet1: Wallet):
|
||||
invoice_payment_hash = str(invoice.payment_hash)
|
||||
invoice_payment_request = invoice.bolt11
|
||||
|
||||
quote = await wallet1.request_melt(invoice_payment_request)
|
||||
quote = await wallet1.melt_quote(invoice_payment_request)
|
||||
total_amount = quote.amount + quote.fee_reserve
|
||||
|
||||
if is_regtest:
|
||||
@@ -421,7 +433,7 @@ async def test_split_invalid_amount(wallet1: Wallet):
|
||||
await wallet1.mint(64, id=invoice.id)
|
||||
await assert_err(
|
||||
wallet1.split(wallet1.proofs, -1),
|
||||
"amount must be positive.",
|
||||
"amount can't be negative",
|
||||
)
|
||||
|
||||
|
||||
@@ -436,13 +448,13 @@ async def test_token_state(wallet1: Wallet):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_mint_keys_specific_keyset(wallet1: Wallet):
|
||||
await wallet1._load_mint_keys()
|
||||
async def testactivate_keyset_specific_keyset(wallet1: Wallet):
|
||||
await wallet1.activate_keyset()
|
||||
assert list(wallet1.keysets.keys()) == ["009a1f293253e41e"]
|
||||
await wallet1._load_mint_keys(keyset_id=wallet1.keyset_id)
|
||||
await wallet1._load_mint_keys(keyset_id="009a1f293253e41e")
|
||||
await wallet1.activate_keyset(keyset_id=wallet1.keyset_id)
|
||||
await wallet1.activate_keyset(keyset_id="009a1f293253e41e")
|
||||
# expect deprecated keyset id to be present
|
||||
await assert_err(
|
||||
wallet1._load_mint_keys(keyset_id="nonexistent"),
|
||||
KeysetNotFoundError(),
|
||||
wallet1.activate_keyset(keyset_id="nonexistent"),
|
||||
KeysetNotFoundError("nonexistent"),
|
||||
)
|
||||
|
||||
@@ -65,16 +65,16 @@ async def test_send(wallet: Wallet):
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_without_split(wallet: Wallet):
|
||||
with TestClient(app) as client:
|
||||
response = client.post("/send?amount=2&nosplit=true")
|
||||
response = client.post("/send?amount=2&offline=true")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["balance"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_regtest, reason="regtest")
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_without_split_but_wrong_amount(wallet: Wallet):
|
||||
async def test_send_too_much(wallet: Wallet):
|
||||
with TestClient(app) as client:
|
||||
response = client.post("/send?amount=10&nosplit=true")
|
||||
response = client.post("/send?amount=110000")
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ def test_invoice_with_split(mint, cli_prefix):
|
||||
wallet = asyncio.run(init_wallet())
|
||||
assert wallet.proof_amounts.count(1) >= 10
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
|
||||
def test_invoices_with_minting(cli_prefix):
|
||||
# arrange
|
||||
@@ -223,6 +224,7 @@ def test_invoices_without_minting(cli_prefix):
|
||||
assert get_invoice_from_invoices_command(result.output)["ID"] == invoice.id
|
||||
assert get_invoice_from_invoices_command(result.output)["Paid"] == str(invoice.paid)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
|
||||
def test_invoices_with_onlypaid_option(cli_prefix):
|
||||
# arrange
|
||||
@@ -263,6 +265,7 @@ def test_invoices_with_onlypaid_option_without_minting(cli_prefix):
|
||||
assert result.exit_code == 0
|
||||
assert "No invoices found." in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
|
||||
def test_invoices_with_onlyunpaid_option(cli_prefix):
|
||||
# arrange
|
||||
@@ -322,6 +325,7 @@ def test_invoices_with_both_onlypaid_and_onlyunpaid_options(cli_prefix):
|
||||
in result.output
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
|
||||
def test_invoices_with_pending_option(cli_prefix):
|
||||
# arrange
|
||||
@@ -422,11 +426,11 @@ def test_send_legacy(mint, cli_prefix):
|
||||
assert token_str.startswith("eyJwcm9v"), "output is not as expected"
|
||||
|
||||
|
||||
def test_send_without_split(mint, cli_prefix):
|
||||
def test_send_offline(mint, cli_prefix):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
cli,
|
||||
[*cli_prefix, "send", "2", "--nosplit"],
|
||||
[*cli_prefix, "send", "2", "--offline"],
|
||||
)
|
||||
assert result.exception is None
|
||||
print("SEND")
|
||||
@@ -434,13 +438,13 @@ def test_send_without_split(mint, cli_prefix):
|
||||
assert "cashuA" in result.output, "output does not have a token"
|
||||
|
||||
|
||||
def test_send_without_split_but_wrong_amount(mint, cli_prefix):
|
||||
def test_send_too_much(mint, cli_prefix):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
cli,
|
||||
[*cli_prefix, "send", "10", "--nosplit"],
|
||||
[*cli_prefix, "send", "100000"],
|
||||
)
|
||||
assert "No proof with this amount found" in str(result.exception)
|
||||
assert "balance too low" in str(result.exception)
|
||||
|
||||
|
||||
def test_receive_tokenv3(mint, cli_prefix):
|
||||
|
||||
@@ -37,7 +37,7 @@ async def reset_wallet_db(wallet: LightningWallet):
|
||||
await wallet.db.execute("DELETE FROM proofs")
|
||||
await wallet.db.execute("DELETE FROM proofs_used")
|
||||
await wallet.db.execute("DELETE FROM keysets")
|
||||
await wallet._load_mint()
|
||||
await wallet.load_mint()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
|
||||
@@ -42,7 +42,7 @@ async def reset_wallet_db(wallet: Wallet):
|
||||
await wallet.db.execute("DELETE FROM proofs")
|
||||
await wallet.db.execute("DELETE FROM proofs_used")
|
||||
await wallet.db.execute("DELETE FROM keysets")
|
||||
await wallet._load_mint()
|
||||
await wallet.load_mint()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
@@ -206,7 +206,7 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet):
|
||||
wallet3.proofs = []
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 100)
|
||||
assert wallet3.balance == 64 * 2
|
||||
assert wallet3.balance == 96
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 64
|
||||
|
||||
@@ -233,7 +233,7 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W
|
||||
assert wallet3.proofs == []
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 100)
|
||||
assert wallet3.balance == 64 + 2 * 32
|
||||
assert wallet3.balance == 96
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 32
|
||||
|
||||
@@ -276,7 +276,7 @@ async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet):
|
||||
assert wallet3.proofs == []
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 100)
|
||||
assert wallet3.balance == 64 + 2 * 32 + 32
|
||||
assert wallet3.balance == 128
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 64
|
||||
|
||||
@@ -311,7 +311,7 @@ async def test_restore_wallet_after_send_twice(
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 10)
|
||||
box.add(wallet3.proofs)
|
||||
assert wallet3.balance == 5
|
||||
assert wallet3.balance == 4
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 2
|
||||
|
||||
@@ -333,7 +333,7 @@ async def test_restore_wallet_after_send_twice(
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 15)
|
||||
box.add(wallet3.proofs)
|
||||
assert wallet3.balance == 7
|
||||
assert wallet3.balance == 6
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 2
|
||||
|
||||
@@ -370,7 +370,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 20)
|
||||
box.add(wallet3.proofs)
|
||||
assert wallet3.balance == 138
|
||||
assert wallet3.balance == 84
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 64
|
||||
|
||||
@@ -389,6 +389,6 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
|
||||
assert wallet3.proofs == []
|
||||
assert wallet3.balance == 0
|
||||
await wallet3.restore_promises_from_to(0, 50)
|
||||
assert wallet3.balance == 182
|
||||
assert wallet3.balance == 108
|
||||
await wallet3.invalidate(wallet3.proofs, check_spendable=True)
|
||||
assert wallet3.balance == 64
|
||||
|
||||
Reference in New Issue
Block a user