Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests: Test with LIGHTNING=True and refactor mint #326

Merged
merged 5 commits into from
Sep 24, 2023
Merged
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
6 changes: 3 additions & 3 deletions cashu/mint/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ async def get_keyset(*args, **kwags):
async def get_lightning_invoice(*args, **kwags):
return await get_lightning_invoice(*args, **kwags) # type: ignore

async def get_proofs_used(*args, **kwags):
return await get_proofs_used(*args, **kwags) # type: ignore
async def get_secrets_used(*args, **kwags):
return await get_secrets_used(*args, **kwags) # type: ignore

async def invalidate_proof(*args, **kwags):
return await invalidate_proof(*args, **kwags) # type: ignore
Expand Down Expand Up @@ -91,7 +91,7 @@ async def get_promise(
return BlindedSignature(amount=row[0], C_=row[2], id=row[3]) if row else None


async def get_proofs_used(
async def get_secrets_used(
db: Database,
conn: Optional[Connection] = None,
):
Expand Down
487 changes: 215 additions & 272 deletions cashu/mint/ledger.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion cashu/mint/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse:
This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu).
"""
logger.trace(f"> POST /checkfees: {payload}")
fees_sat = await ledger.check_fees(payload.pr)
fees_sat = await ledger.get_melt_fees(payload.pr)
logger.trace(f"< POST /checkfees: {fees_sat}")
return CheckFeesResponse(fee=fees_sat)

Expand Down
37 changes: 28 additions & 9 deletions cashu/mint/verification.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Literal, Optional, Union
from typing import List, Literal, Optional, Set, Union

from loguru import logger

Expand All @@ -15,6 +15,7 @@
NoSecretInProofsError,
NotAllowedError,
SecretTooLongError,
TokenAlreadySpentError,
TransactionError,
)
from ..core.settings import settings
Expand All @@ -27,8 +28,9 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):

keyset: MintKeyset
keysets: MintKeysets
secrets_used: Set[str]

async def _verify_proofs_and_outputs(
async def verify_inputs_and_outputs(
self, proofs: List[Proof], outputs: Optional[List[BlindedMessage]] = None
):
"""Checks all proofs and outputs for validity.
Expand All @@ -45,34 +47,51 @@ async def _verify_proofs_and_outputs(
Exception: BDHKE verification failed.
"""
# Verify inputs

# Verify proofs are spendable
self._check_proofs_spendable(proofs)
# Verify amounts of inputs
if not all([self._verify_amount(p.amount) for p in proofs]):
raise TransactionError("invalid amount.")
# Verify secret criteria
if not all([self._verify_secret_criteria(p) for p in proofs]):
raise TransactionError("secrets do not match criteria.")
# verify that only unique proofs were used
if not self._verify_no_duplicate_proofs(proofs):
raise TransactionError("duplicate proofs.")
# Verify input spending conditions
if not all([self._verify_input_spending_conditions(p) for p in proofs]):
raise TransactionError("validation of input spending conditions failed.")
# Verify ecash signatures
if not all([self._verify_proof_bdhke(p) for p in proofs]):
raise TransactionError("could not verify proofs.")
# Verify input spending conditions
if not all([self._verify_input_spending_conditions(p) for p in proofs]):
raise TransactionError("validation of input spending conditions failed.")

if not outputs:
return

# Verify outputs
self._verify_outputs(outputs)

# verify that only unique outputs were used
if not self._verify_no_duplicate_outputs(outputs):
raise TransactionError("duplicate promises.")
# Verify inputs and outputs together
if not self._verify_input_output_amounts(proofs, outputs):
raise TransactionError("input amounts less than output.")
# Verify output spending conditions
if outputs and not self._verify_output_spending_conditions(proofs, outputs):
raise TransactionError("validation of output spending conditions failed.")

def _verify_outputs(self, outputs: List[BlindedMessage]):
"""Verify that the outputs are valid."""
# Verify amounts of outputs
if not all([self._verify_amount(o.amount) for o in outputs]):
raise TransactionError("invalid amount.")
# verify that only unique outputs were used
if not self._verify_no_duplicate_outputs(outputs):
raise TransactionError("duplicate outputs.")

def _check_proofs_spendable(self, proofs: List[Proof]):
"""Checks whether the proofs were already spent."""
if not all([p.secret not in self.secrets_used for p in proofs]):
raise TokenAlreadySpentError()

def _verify_secret_criteria(self, proof: Proof) -> Literal[True]:
"""Verifies that a secret is present and is not too long (DOS prevention)."""
if proof.secret is None or proof.secret == "":
Expand Down
3 changes: 3 additions & 0 deletions cashu/wallet/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ async def generate_n_secrets(
Tuple[List[str], List[PrivateKey], List[str]]: Secrets, blinding factors, derivation paths

"""
if n < 1:
return [], [], []

secret_counters_start = await bump_secret_derivation(
db=self.db, keyset_id=self.keyset_id, by=n, skip=skip_bump
)
Expand Down
3 changes: 3 additions & 0 deletions cashu/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,9 @@ async def split_to_send(
set_reserved (bool, optional): If set, the proofs are marked as reserved. Should be set to False if a payment attempt
is made with the split that could fail (like a Lightning payment). Should be set to True if the token to be sent is
displayed to the user to be then sent to someone else. Defaults to False.

Returns:
Tuple[List[Proof], List[Proof]]: Tuple of proofs to keep and proofs to send
"""
if secret_lock:
logger.debug(f"Spending conditions: {secret_lock}")
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
settings.mint_host = "0.0.0.0"
settings.mint_listen_port = SERVER_PORT
settings.mint_url = SERVER_ENDPOINT
settings.lightning = False
settings.lightning = True
settings.tor = False
settings.mint_lightning_backend = "FakeWallet"
settings.mint_database = "./test_data/test_mint"
Expand Down
7 changes: 5 additions & 2 deletions tests/test_mint.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,14 @@ async def test_get_keyset(ledger: Ledger):

@pytest.mark.asyncio
async def test_mint(ledger: Ledger):
invoice, payment_hash = await ledger.request_mint(8)
blinded_messages_mock = [
BlindedMessage(
amount=8,
B_="02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239",
)
]
promises = await ledger.mint(blinded_messages_mock)
promises = await ledger.mint(blinded_messages_mock, hash=payment_hash)
assert len(promises)
assert promises[0].amount == 8
assert (
Expand All @@ -83,14 +84,16 @@ async def test_mint(ledger: Ledger):

@pytest.mark.asyncio
async def test_mint_invalid_blinded_message(ledger: Ledger):
invoice, payment_hash = await ledger.request_mint(8)
blinded_messages_mock_invalid_key = [
BlindedMessage(
amount=8,
B_="02634a2c2b34bec9e8a4aba4361f6bff02d7fa2365379b0840afe249a7a9d71237",
)
]
await assert_err(
ledger.mint(blinded_messages_mock_invalid_key), "invalid public key"
ledger.mint(blinded_messages_mock_invalid_key, hash=payment_hash),
"invalid public key",
)


Expand Down
Loading