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

* refactor mint verification

* test with lightning=true

* rename proofs_used to secrets_used and refactor

* test with lightning

* spelling fixes
This commit is contained in:
callebtc
2023-09-24 14:35:13 +02:00
committed by GitHub
parent f1b621fa90
commit 638324940a
12 changed files with 379 additions and 345 deletions

View File

@@ -17,8 +17,8 @@ class LedgerCrud:
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
@@ -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,
):

View File

@@ -24,7 +24,6 @@ from ..core.errors import (
KeysetNotFoundError,
LightningError,
NotAllowedError,
TokenAlreadySpentError,
TransactionError,
)
from ..core.helpers import fee_reserve, sum_proofs
@@ -50,7 +49,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
derivation_path="",
crud=LedgerCrud,
):
self.proofs_used: Set[str] = set()
self.secrets_used: Set[str] = set()
self.master_key = seed
self.derivation_path = derivation_path
@@ -60,12 +59,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
self.pubkey = derive_pubkey(self.master_key)
self.keysets = MintKeysets([])
async def load_used_proofs(self):
"""Load all used proofs from database."""
logger.trace("crud: loading used proofs")
proofs_used = await self.crud.get_proofs_used(db=self.db)
logger.trace(f"crud: loaded {len(proofs_used)} used proofs")
self.proofs_used = set(proofs_used)
# ------- KEYS -------
async def load_keyset(self, derivation_path, autosave=True) -> MintKeyset:
"""Load the keyset for a derivation path if it already exists. If not generate new one and store in the db.
@@ -144,77 +138,15 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
# load the current keyset
self.keyset = await self.load_keyset(self.derivation_path, autosave)
async def _generate_promises(
self, B_s: List[BlindedMessage], keyset: Optional[MintKeyset] = None
) -> list[BlindedSignature]:
"""Generates promises that sum to the given amount.
def get_keyset(self, keyset_id: Optional[str] = None):
"""Returns a dictionary of hex public keys of a specific keyset for each supported amount"""
if keyset_id and keyset_id not in self.keysets.keysets:
raise KeysetNotFoundError()
keyset = self.keysets.keysets[keyset_id] if keyset_id else self.keyset
assert keyset.public_keys, KeysetError("no public keys for this keyset")
return {a: p.serialize().hex() for a, p in keyset.public_keys.items()}
Args:
B_s (List[BlindedMessage]): _description_
keyset (Optional[MintKeyset], optional): _description_. Defaults to None.
Returns:
list[BlindedSignature]: _description_
"""
return [
await self._generate_promise(
b.amount, PublicKey(bytes.fromhex(b.B_), raw=True), keyset
)
for b in B_s
]
async def _generate_promise(
self, amount: int, B_: PublicKey, keyset: Optional[MintKeyset] = None
) -> BlindedSignature:
"""Generates a promise (Blind signature) for given amount and returns a pair (amount, C').
Args:
amount (int): Amount of the promise.
B_ (PublicKey): Blinded secret (point on curve)
keyset (Optional[MintKeyset], optional): Which keyset to use. Private keys will be taken from this keyset. Defaults to None.
Returns:
BlindedSignature: Generated promise.
"""
keyset = keyset if keyset else self.keyset
logger.trace(f"Generating promise with keyset {keyset.id}.")
private_key_amount = keyset.private_keys[amount]
C_, e, s = b_dhke.step2_bob(B_, private_key_amount)
logger.trace(f"crud: _generate_promise storing promise for {amount}")
await self.crud.store_promise(
amount=amount,
B_=B_.serialize().hex(),
C_=C_.serialize().hex(),
e=e.serialize(),
s=s.serialize(),
db=self.db,
id=keyset.id,
)
logger.trace(f"crud: _generate_promise stored promise for {amount}")
return BlindedSignature(
id=keyset.id,
amount=amount,
C_=C_.serialize().hex(),
dleq=DLEQ(e=e.serialize(), s=s.serialize()),
)
def _check_proofs_spendable(self, proofs: List[Proof]):
"""Checks whether the proofs were already spent."""
if not all([p.secret not in self.proofs_used for p in proofs]):
raise TokenAlreadySpentError()
def _check_spendable(self, proof: Proof):
"""Checks whether the proof was already spent."""
return proof.secret not in self.proofs_used
async def _check_pending(self, proofs: List[Proof]):
"""Checks whether the proof is still pending."""
proofs_pending = await self.crud.get_proofs_pending(db=self.db)
pending_secrets = [pp.secret for pp in proofs_pending]
pending_states = [
True if p.secret in pending_secrets else False for p in proofs
]
return pending_states
# ------- LIGHTNING -------
async def _request_lightning_invoice(self, amount: int):
"""Generate a Lightning invoice using the funding source backend.
@@ -266,50 +198,26 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
Returns:
bool: True if invoice has been paid, else False
"""
logger.trace(f"crud: _check_lightning_invoice: checking invoice {hash}")
invoice: Union[Invoice, None] = await self.crud.get_lightning_invoice(
hash=hash, db=self.db, conn=conn
)
logger.trace(f"crud: _check_lightning_invoice: invoice: {invoice}")
if invoice is None:
raise LightningError("invoice not found.")
if invoice.issued:
raise LightningError("tokens already issued for this invoice.")
if amount > invoice.amount:
raise LightningError(
f"requested amount too high: {amount}. Invoice amount: {invoice.amount}"
)
assert invoice.payment_hash, "invoice has no payment hash."
# set this invoice as issued
logger.trace(f"crud: setting invoice {invoice.payment_hash} as issued")
await self.crud.update_lightning_invoice(
hash=hash, issued=True, db=self.db, conn=conn
status = await self.lightning.get_invoice_status(invoice.payment_hash)
logger.trace(
f"_check_lightning_invoice: invoice {invoice.payment_hash} status: {status}"
)
logger.trace(f"crud: invoice {invoice.payment_hash} set as issued")
if not status.paid:
raise InvoiceNotPaidError()
try:
if amount > invoice.amount:
raise LightningError(
f"requested amount too high: {amount}. Invoice amount:"
f" {invoice.amount}"
)
logger.trace(
f"_check_lightning_invoice: checking invoice {invoice.payment_hash}"
)
status = await self.lightning.get_invoice_status(invoice.payment_hash)
logger.trace(
f"_check_lightning_invoice: invoice {invoice.payment_hash} status:"
f" {status}"
)
if status.paid:
return status.paid
else:
raise InvoiceNotPaidError()
except Exception as e:
# unset issued
logger.trace(f"crud: unsetting invoice {invoice.payment_hash} as issued")
await self.crud.update_lightning_invoice(
hash=hash, issued=False, db=self.db, conn=conn
)
logger.trace(f"crud: invoice {invoice.payment_hash} unset as issued")
raise e
return status.paid
async def _pay_lightning_invoice(self, invoice: str, fee_limit_msat: int):
"""Pays a Lightning invoice via the funding source backend.
@@ -324,9 +232,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
Returns:
Tuple[bool, string, int]: Returns payment status, preimage of invoice, paid fees (in Millisatoshi)
"""
logger.trace(f"_pay_lightning_invoice: paying Lightning invoice {invoice}")
error, balance = await self.lightning.status()
logger.trace(f"_pay_lightning_invoice: Lightning wallet balance: {balance}")
if error:
raise LightningError(f"Lightning wallet not responding: {error}")
(
@@ -341,6 +247,8 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
fee_msat = abs(fee_msat) if fee_msat else fee_msat
return ok, preimage, fee_msat
# ------- ECASH -------
async def _invalidate_proofs(self, proofs: List[Proof]):
"""Adds secrets of proofs to the list of known secrets and stores them in the db.
Removes proofs from pending table. This is executed if the ecash has been redeemed.
@@ -349,84 +257,11 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
proofs (List[Proof]): Proofs to add to known secret table.
"""
# Mark proofs as used and prepare new promises
proof_msgs = set([p.secret for p in proofs])
self.proofs_used |= proof_msgs
secrets = set([p.secret for p in proofs])
self.secrets_used |= secrets
# store in db
logger.trace("crud: storing proofs")
for p in proofs:
await self.crud.invalidate_proof(proof=p, db=self.db)
logger.trace("crud: stored proofs")
async def _set_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to
the list of pending proofs or removes them. Used as a mutex for proofs.
Args:
proofs (List[Proof]): Proofs to add to pending table.
Raises:
Exception: At least one proof already in pending table.
"""
# first we check whether these proofs are pending aready
async with self.proofs_pending_lock:
await self._validate_proofs_pending(proofs, conn)
for p in proofs:
try:
logger.trace(
f"crud: _set_proofs_pending setting proof {p.secret} as pending"
)
await self.crud.set_proof_pending(proof=p, db=self.db, conn=conn)
logger.trace(
f"crud: _set_proofs_pending proof {p.secret} set as pending"
)
except Exception:
raise TransactionError("proofs already pending.")
async def _unset_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""Deletes proofs from pending table.
Args:
proofs (List[Proof]): Proofs to delete.
"""
# we try: except: this block in order to avoid that any errors here
# could block the _invalidate_proofs() call that happens afterwards.
async with self.proofs_pending_lock:
try:
for p in proofs:
logger.trace(
f"crud: _unset_proofs_pending unsetting proof {p.secret} as"
" pending"
)
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)
logger.trace(
f"crud: _unset_proofs_pending proof {p.secret} unset as pending"
)
except Exception as e:
print(e)
pass
async def _validate_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""Checks if any of the provided proofs is in the pending proofs table.
Args:
proofs (List[Proof]): Proofs to check.
Raises:
Exception: At least one of the proofs is in the pending table.
"""
logger.trace("crud: _validate_proofs_pending validating proofs")
proofs_pending = await self.crud.get_proofs_pending(db=self.db, conn=conn)
logger.trace("crud: _validate_proofs_pending got proofs pending")
for p in proofs:
for pp in proofs_pending:
if p.secret == pp.secret:
raise TransactionError("proofs are pending.")
async def _generate_change_promises(
self,
@@ -490,14 +325,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
else:
return []
# Public methods
def get_keyset(self, keyset_id: Optional[str] = None):
"""Returns a dictionary of hex public keys of a specific keyset for each supported amount"""
if keyset_id and keyset_id not in self.keysets.keysets:
raise KeysetNotFoundError()
keyset = self.keysets.keysets[keyset_id] if keyset_id else self.keyset
assert keyset.public_keys, KeysetError("no public keys for this keyset")
return {a: p.serialize().hex() for a, p in keyset.public_keys.items()}
# ------- TRANSACTIONS -------
async def request_mint(self, amount: int):
"""Returns Lightning invoice and stores it in the db.
@@ -561,8 +389,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
List[BlindedSignature]: Signatures on the outputs.
"""
logger.trace("called mint")
amounts = [b.amount for b in B_s]
amount = sum(amounts)
amount_outputs = sum([b.amount for b in B_s])
if settings.lightning:
if not hash:
@@ -571,15 +398,17 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
self.locks.get(hash) or asyncio.Lock()
) # create a new lock if it doesn't exist
async with self.locks[hash]:
# will raise an exception if the invoice is not paid or tokens are already issued
await self._check_lightning_invoice(amount, hash)
# will raise an exception if the invoice is not paid or tokens are
# already issued or the requested amount is too high
await self._check_lightning_invoice(amount_outputs, hash)
logger.trace(f"crud: setting invoice {hash} as issued")
await self.crud.update_lightning_invoice(
hash=hash, issued=True, db=self.db
)
del self.locks[hash]
for amount in amounts:
if amount not in [2**i for i in range(settings.max_order)]:
raise NotAllowedError(
f"Can only mint amounts with 2^n up to {2**settings.max_order}."
)
self._verify_outputs(B_s)
promises = await self._generate_promises(B_s, keyset)
logger.trace("generated promises")
@@ -615,49 +444,45 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
raise NotAllowedError(
f"Maximum melt amount is {settings.mint_max_peg_out} sat."
)
fees_msat = await self.check_fees(invoice)
fees_sat = await self.get_melt_fees(invoice)
# verify overspending attempt
assert (
total_provided >= invoice_amount + fees_msat / 1000
), TransactionError("provided proofs not enough for Lightning payment.")
assert total_provided >= invoice_amount + fees_sat, TransactionError(
"provided proofs not enough for Lightning payment. Provided:"
f" {total_provided}, needed: {invoice_amount + fees_sat}"
)
# verify that proofs have not been spent yet
self._check_proofs_spendable(proofs)
# verify spending inputs, outputs, and spending conditions
await self._verify_proofs_and_outputs(proofs, outputs)
await self.verify_inputs_and_outputs(proofs, outputs)
# promises to return for overpaid fees
return_promises: List[BlindedSignature] = []
if settings.lightning:
logger.trace("paying lightning invoice")
logger.trace(f"paying lightning invoice {invoice}")
status, preimage, fee_msat = await self._pay_lightning_invoice(
invoice, fees_msat
invoice, fees_sat * 1000
)
logger.trace("paid lightning invoice")
else:
status, preimage, fee_msat = True, "preimage", 0
logger.trace(
f"status: {status}, preimage: {preimage}, fee_msat: {fee_msat}"
logger.debug(
f"Melt status: {status}: preimage: {preimage}, fee_msat: {fee_msat}"
)
if status:
logger.trace("invalidating proofs")
await self._invalidate_proofs(proofs)
logger.trace("invalidated proofs")
# prepare change to compensate wallet for overpaid fees
assert fee_msat is not None, TransactionError("fees not valid")
if outputs:
return_promises = await self._generate_change_promises(
total_provided=total_provided,
invoice_amount=invoice_amount,
ln_fee_msat=fee_msat,
outputs=outputs,
)
else:
logger.trace("lightning payment unsuccessful")
if not status:
raise LightningError("Lightning payment unsuccessful.")
# melt successful, invalidate proofs
await self._invalidate_proofs(proofs)
# prepare change to compensate wallet for overpaid fees
return_promises: List[BlindedSignature] = []
if outputs and fee_msat:
return_promises = await self._generate_change_promises(
total_provided=total_provided,
invoice_amount=invoice_amount,
ln_fee_msat=fee_msat,
outputs=outputs,
)
except Exception as e:
logger.trace(f"exception: {e}")
raise e
@@ -667,28 +492,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
return status, preimage, return_promises
async def check_proof_state(
self, proofs: List[Proof]
) -> Tuple[List[bool], List[bool]]:
"""Checks if provided proofs are spend or are pending.
Used by wallets to check if their proofs have been redeemed by a receiver or they are still in-flight in a transaction.
Returns two lists that are in the same order as the provided proofs. Wallet must match the list
to the proofs they have provided in order to figure out which proof is spendable or pending
and which isn't.
Args:
proofs (List[Proof]): List of proofs to check.
Returns:
List[bool]: List of which proof is still spendable (True if still spendable, else False)
List[bool]: List of which proof are pending (True if pending, else False)
"""
spendable = [self._check_spendable(p) for p in proofs]
pending = await self._check_pending(proofs)
return spendable, pending
async def check_fees(self, pr: str):
async def get_melt_fees(self, pr: str) -> int:
"""Returns the fee reserve (in sat) that a wallet must add to its proofs
in order to pay a Lightning invoice.
@@ -702,18 +506,18 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
# if id does not exist (not internal), it returns paid = None
if settings.lightning:
decoded_invoice = bolt11.decode(pr)
amount = math.ceil(decoded_invoice.amount_msat / 1000)
amount_msat = decoded_invoice.amount_msat
logger.trace(
"check_fees: checking lightning invoice:"
"get_melt_fees: checking lightning invoice:"
f" {decoded_invoice.payment_hash}"
)
paid = await self.lightning.get_invoice_status(decoded_invoice.payment_hash)
logger.trace(f"check_fees: paid: {paid}")
logger.trace(f"get_melt_fees: paid: {paid}")
internal = paid.paid is False
else:
amount = 0
amount_msat = 0
internal = True
fees_msat = fee_reserve(amount * 1000, internal)
fees_msat = fee_reserve(amount_msat, internal)
fee_sat = math.ceil(fees_msat / 1000)
return fee_sat
@@ -743,21 +547,11 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
logger.trace("split called")
await self._set_proofs_pending(proofs)
total_amount = sum_proofs(proofs)
try:
# verify that amount is kosher
self._verify_amount(total_amount)
# verify overspending attempt
self._verify_equation_balanced(proofs, outputs)
# verify that proofs have not been spent yet
self._check_proofs_spendable(proofs)
# verify spending inputs, outputs, and spending conditions
await self._verify_proofs_and_outputs(proofs, outputs)
await self.verify_inputs_and_outputs(proofs, outputs)
# Mark proofs as used and prepare new promises
await self._invalidate_proofs(proofs)
except Exception as e:
logger.trace(f"split failed: {e}")
raise e
@@ -815,3 +609,152 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
return_outputs.append(output)
logger.trace(f"promise found: {promise}")
return return_outputs, promises
# ------- BLIND SIGNATURES -------
async def _generate_promises(
self, B_s: List[BlindedMessage], keyset: Optional[MintKeyset] = None
) -> list[BlindedSignature]:
"""Generates promises that sum to the given amount.
Args:
B_s (List[BlindedMessage]): _description_
keyset (Optional[MintKeyset], optional): _description_. Defaults to None.
Returns:
list[BlindedSignature]: _description_
"""
return [
await self._generate_promise(
b.amount, PublicKey(bytes.fromhex(b.B_), raw=True), keyset
)
for b in B_s
]
async def _generate_promise(
self, amount: int, B_: PublicKey, keyset: Optional[MintKeyset] = None
) -> BlindedSignature:
"""Generates a promise (Blind signature) for given amount and returns a pair (amount, C').
Args:
amount (int): Amount of the promise.
B_ (PublicKey): Blinded secret (point on curve)
keyset (Optional[MintKeyset], optional): Which keyset to use. Private keys will be taken from this keyset. Defaults to None.
Returns:
BlindedSignature: Generated promise.
"""
keyset = keyset if keyset else self.keyset
logger.trace(f"Generating promise with keyset {keyset.id}.")
private_key_amount = keyset.private_keys[amount]
C_, e, s = b_dhke.step2_bob(B_, private_key_amount)
logger.trace(f"crud: _generate_promise storing promise for {amount}")
await self.crud.store_promise(
amount=amount,
B_=B_.serialize().hex(),
C_=C_.serialize().hex(),
e=e.serialize(),
s=s.serialize(),
db=self.db,
id=keyset.id,
)
logger.trace(f"crud: _generate_promise stored promise for {amount}")
return BlindedSignature(
id=keyset.id,
amount=amount,
C_=C_.serialize().hex(),
dleq=DLEQ(e=e.serialize(), s=s.serialize()),
)
# ------- PROOFS -------
async def load_used_proofs(self):
"""Load all used proofs from database."""
logger.trace("crud: loading used proofs")
secrets_used = await self.crud.get_secrets_used(db=self.db)
logger.trace(f"crud: loaded {len(secrets_used)} used proofs")
self.secrets_used = set(secrets_used)
def _check_spendable(self, proof: Proof):
"""Checks whether the proof was already spent."""
return proof.secret not in self.secrets_used
async def _check_pending(self, proofs: List[Proof]):
"""Checks whether the proof is still pending."""
proofs_pending = await self.crud.get_proofs_pending(db=self.db)
pending_secrets = [pp.secret for pp in proofs_pending]
pending_states = [
True if p.secret in pending_secrets else False for p in proofs
]
return pending_states
async def check_proof_state(
self, proofs: List[Proof]
) -> Tuple[List[bool], List[bool]]:
"""Checks if provided proofs are spend or are pending.
Used by wallets to check if their proofs have been redeemed by a receiver or they are still in-flight in a transaction.
Returns two lists that are in the same order as the provided proofs. Wallet must match the list
to the proofs they have provided in order to figure out which proof is spendable or pending
and which isn't.
Args:
proofs (List[Proof]): List of proofs to check.
Returns:
List[bool]: List of which proof is still spendable (True if still spendable, else False)
List[bool]: List of which proof are pending (True if pending, else False)
"""
spendable = [self._check_spendable(p) for p in proofs]
pending = await self._check_pending(proofs)
return spendable, pending
async def _set_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to
the list of pending proofs or removes them. Used as a mutex for proofs.
Args:
proofs (List[Proof]): Proofs to add to pending table.
Raises:
Exception: At least one proof already in pending table.
"""
# first we check whether these proofs are pending aready
async with self.proofs_pending_lock:
await self._validate_proofs_pending(proofs, conn)
for p in proofs:
try:
await self.crud.set_proof_pending(proof=p, db=self.db, conn=conn)
except Exception:
raise TransactionError("proofs already pending.")
async def _unset_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""Deletes proofs from pending table.
Args:
proofs (List[Proof]): Proofs to delete.
"""
async with self.proofs_pending_lock:
for p in proofs:
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)
async def _validate_proofs_pending(
self, proofs: List[Proof], conn: Optional[Connection] = None
):
"""Checks if any of the provided proofs is in the pending proofs table.
Args:
proofs (List[Proof]): Proofs to check.
Raises:
Exception: At least one of the proofs is in the pending table.
"""
proofs_pending = await self.crud.get_proofs_pending(db=self.db, conn=conn)
for p in proofs:
for pp in proofs_pending:
if p.secret == pp.secret:
raise TransactionError("proofs are pending.")

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
from typing import List, Literal, Optional, Union
from typing import List, Literal, Optional, Set, Union
from loguru import logger
@@ -15,6 +15,7 @@ from ..core.errors import (
NoSecretInProofsError,
NotAllowedError,
SecretTooLongError,
TokenAlreadySpentError,
TransactionError,
)
from ..core.settings import settings
@@ -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.
@@ -45,34 +47,51 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):
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 == "":

View File

@@ -148,6 +148,9 @@ class WalletSecrets(SupportsDb, SupportsKeysets):
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
)

View File

@@ -1220,6 +1220,9 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
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}")

View File

@@ -25,7 +25,7 @@ settings.mint_port = SERVER_PORT
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"

View File

@@ -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 (
@@ -83,6 +84,7 @@ 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,
@@ -90,7 +92,8 @@ async def test_mint_invalid_blinded_message(ledger: Ledger):
)
]
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",
)

View File

@@ -136,15 +136,17 @@ async def test_get_keyset_ids(wallet1: Wallet):
@pytest.mark.asyncio
async def test_mint(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
assert wallet1.balance == 64
@pytest.mark.asyncio
async def test_mint_amounts(wallet1: Wallet):
"""Mint predefined amounts"""
invoice = await wallet1.request_mint(64)
amts = [1, 1, 1, 2, 2, 4, 16]
await wallet1.mint(amount=sum(amts), split=amts)
await wallet1.mint(amount=sum(amts), split=amts, hash=invoice.hash)
assert wallet1.balance == 27
assert wallet1.proof_amounts == amts
@@ -171,7 +173,8 @@ async def test_mint_amounts_wrong_order(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
assert wallet1.balance == 64
p1, p2 = await wallet1.split(wallet1.proofs, 20)
assert wallet1.balance == 64
@@ -185,7 +188,8 @@ async def test_split(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split_to_send(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
keep_proofs, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True
)
@@ -199,7 +203,8 @@ async def test_split_to_send(wallet1: Wallet):
@pytest.mark.asyncio
async def test_split_more_than_balance(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await assert_err(
wallet1.split(wallet1.proofs, 128),
# "Mint Error: inputs do not have same amount as outputs",
@@ -208,9 +213,27 @@ async def test_split_more_than_balance(wallet1: Wallet):
assert wallet1.balance == 64
@pytest.mark.asyncio
async def test_melt(wallet1: Wallet):
# mint twice so we have enough to pay the second invoice back
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
assert wallet1.balance == 128
total_amount, fee_reserve_sat = await wallet1.get_pay_amount_with_fees(invoice.pr)
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, total_amount)
await wallet1.pay_lightning(
send_proofs, invoice=invoice.pr, fee_reserve_sat=fee_reserve_sat
)
assert wallet1.balance == 128 - total_amount
@pytest.mark.asyncio
async def test_split_to_send_more_than_balance(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await assert_err(
wallet1.split_to_send(wallet1.proofs, 128, set_reserved=True),
"balance too low.",
@@ -221,7 +244,8 @@ async def test_split_to_send_more_than_balance(wallet1: Wallet):
@pytest.mark.asyncio
async def test_double_spend(wallet1: Wallet):
doublespend = await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
doublespend = await wallet1.mint(64, hash=invoice.hash)
await wallet1.split(wallet1.proofs, 20)
await assert_err(
wallet1.split(doublespend, 20),
@@ -233,7 +257,8 @@ async def test_double_spend(wallet1: Wallet):
@pytest.mark.asyncio
async def test_duplicate_proofs_double_spent(wallet1: Wallet):
doublespend = await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
doublespend = await wallet1.mint(64, hash=invoice.hash)
await assert_err(
wallet1.split(wallet1.proofs + doublespend, 20),
"Mint Error: proofs already pending.",
@@ -244,7 +269,8 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet):
@pytest.mark.asyncio
async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
_, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True
)
@@ -261,7 +287,8 @@ async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_invalidate_unspent_proofs(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet. Should not work!"""
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await wallet1.invalidate(wallet1.proofs)
assert wallet1.balance == 64
@@ -269,14 +296,16 @@ async def test_invalidate_unspent_proofs(wallet1: Wallet):
@pytest.mark.asyncio
async def test_invalidate_unspent_proofs_without_checking(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet but force no check."""
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await wallet1.invalidate(wallet1.proofs, check_spendable=False)
assert wallet1.balance == 0
@pytest.mark.asyncio
async def test_split_invalid_amount(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await assert_err(
wallet1.split(wallet1.proofs, -1),
"amount must be positive.",
@@ -285,14 +314,16 @@ async def test_split_invalid_amount(wallet1: Wallet):
@pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio
async def test_p2sh(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
_ = await wallet1.create_p2sh_address_and_store() # receiver side
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8) # sender side
@@ -305,7 +336,8 @@ async def test_p2sh(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
wallet1_address = await wallet1.create_p2sh_address_and_store() # receiver side
secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side
_, send_proofs = await wallet1.split_to_send(
@@ -316,7 +348,8 @@ async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_token_state(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
assert wallet1.balance == 64
resp = await wallet1.check_proof_state(wallet1.proofs)
assert resp.dict()["spendable"]
@@ -329,11 +362,11 @@ async def test_bump_secret_derivation(wallet3: Wallet):
"half depart obvious quality work element tank gorilla view sugar picture"
" humble"
)
secrets1, rs1, derivaion_paths1 = await wallet3.generate_n_secrets(5)
secrets2, rs2, derivaion_paths2 = await wallet3.generate_secrets_from_to(0, 4)
secrets1, rs1, derivation_paths1 = await wallet3.generate_n_secrets(5)
secrets2, rs2, derivation_paths2 = await wallet3.generate_secrets_from_to(0, 4)
assert secrets1 == secrets2
assert [r.private_key for r in rs1] == [r.private_key for r in rs2]
assert derivaion_paths1 == derivaion_paths2
assert derivation_paths1 == derivation_paths2
assert secrets1 == [
"9bfb12704297fe90983907d122838940755fcce370ce51e9e00a4275a347c3fe",
"dbc5e05f2b1f24ec0e2ab6e8312d5e13f57ada52594d4caf429a697d9c742490",
@@ -341,7 +374,7 @@ async def test_bump_secret_derivation(wallet3: Wallet):
"652d08c804bd2c5f2c1f3e3d8895860397df394b30473753227d766affd15e89",
"654e5997f8a20402f7487296b6f7e463315dd52fc6f6cc5a4e35c7f6ccac77e0",
]
assert derivaion_paths1 == [
assert derivation_paths1 == [
"m/129372'/0'/2004500376'/0'",
"m/129372'/0'/2004500376'/1'",
"m/129372'/0'/2004500376'/2'",
@@ -356,11 +389,11 @@ async def test_bump_secret_derivation_two_steps(wallet3: Wallet):
"half depart obvious quality work element tank gorilla view sugar picture"
" humble"
)
secrets1_1, rs1_1, derivaion_paths1 = await wallet3.generate_n_secrets(2)
secrets1_2, rs1_2, derivaion_paths2 = await wallet3.generate_n_secrets(3)
secrets1_1, rs1_1, derivation_paths1 = await wallet3.generate_n_secrets(2)
secrets1_2, rs1_2, derivation_paths2 = await wallet3.generate_n_secrets(3)
secrets1 = secrets1_1 + secrets1_2
rs1 = rs1_1 + rs1_2
secrets2, rs2, derivaion_paths = await wallet3.generate_secrets_from_to(0, 4)
secrets2, rs2, derivation_paths = await wallet3.generate_secrets_from_to(0, 4)
assert secrets1 == secrets2
assert [r.private_key for r in rs1] == [r.private_key for r in rs2]
@@ -371,9 +404,9 @@ async def test_generate_secrets_from_to(wallet3: Wallet):
"half depart obvious quality work element tank gorilla view sugar picture"
" humble"
)
secrets1, rs1, derivaion_paths1 = await wallet3.generate_secrets_from_to(0, 4)
secrets1, rs1, derivation_paths1 = await wallet3.generate_secrets_from_to(0, 4)
assert len(secrets1) == 5
secrets2, rs2, derivaion_paths2 = await wallet3.generate_secrets_from_to(2, 4)
secrets2, rs2, derivation_paths2 = await wallet3.generate_secrets_from_to(2, 4)
assert len(secrets2) == 3
assert secrets1[2:] == secrets2
assert [r.private_key for r in rs1[2:]] == [r.private_key for r in rs2]
@@ -382,7 +415,8 @@ async def test_generate_secrets_from_to(wallet3: Wallet):
@pytest.mark.asyncio
async def test_restore_wallet_after_mint(wallet3: Wallet):
await reset_wallet_db(wallet3)
await wallet3.mint(64)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64, hash=invoice.hash)
assert wallet3.balance == 64
await reset_wallet_db(wallet3)
await wallet3.load_proofs()
@@ -411,7 +445,8 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet):
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64, hash=invoice.hash)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
@@ -432,8 +467,8 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W
"hello rug want adapt talent together lunar method bean expose beef position"
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64, hash=invoice.hash)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
@@ -472,7 +507,8 @@ async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet):
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64, hash=invoice.hash)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
@@ -497,7 +533,8 @@ async def test_restore_wallet_after_send_twice(
wallet3.private_key = PrivateKey()
await reset_wallet_db(wallet3)
await wallet3.mint(2)
invoice = await wallet3.request_mint(2)
await wallet3.mint(2, hash=invoice.hash)
box.add(wallet3.proofs)
assert wallet3.balance == 2
@@ -550,7 +587,8 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64, hash=invoice.hash)
box.add(wallet3.proofs)
assert wallet3.balance == 64

View File

@@ -58,7 +58,8 @@ async def wallet2(mint):
@pytest.mark.asyncio
async def test_create_htlc_secret(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage)
@@ -67,7 +68,8 @@ async def test_create_htlc_secret(wallet1: Wallet):
@pytest.mark.asyncio
async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage)
@@ -79,7 +81,8 @@ async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage)
@@ -92,7 +95,8 @@ async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage[:-1] + "1")
@@ -107,7 +111,8 @@ async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet)
@pytest.mark.asyncio
async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -126,7 +131,8 @@ async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -149,7 +155,8 @@ async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet
@pytest.mark.asyncio
async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
@@ -171,7 +178,8 @@ async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wall
async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
wallet1: Wallet, wallet2: Wallet
):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
@@ -205,7 +213,8 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature(
wallet1: Wallet, wallet2: Wallet
):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()

View File

@@ -59,14 +59,16 @@ async def wallet2(mint):
@pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio
async def test_p2pk(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test
secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # sender side
@@ -78,7 +80,8 @@ async def test_p2pk(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2pk_receive_with_wrong_private_key(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # sender side
@@ -97,7 +100,8 @@ async def test_p2pk_receive_with_wrong_private_key(wallet1: Wallet, wallet2: Wal
async def test_p2pk_short_locktime_receive_with_wrong_private_key(
wallet1: Wallet, wallet2: Wallet
):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
secret_lock = await wallet1.create_p2pk_lock(
@@ -121,7 +125,8 @@ async def test_p2pk_short_locktime_receive_with_wrong_private_key(
@pytest.mark.asyncio
async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
garbage_pubkey = PrivateKey().pubkey
@@ -148,7 +153,8 @@ async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet
@pytest.mark.asyncio
async def test_p2pk_locktime_with_wrong_refund_pubkey(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await wallet2.create_p2pk_pubkey() # receiver side
# sender side
garbage_pubkey = PrivateKey().pubkey
@@ -182,7 +188,8 @@ async def test_p2pk_locktime_with_wrong_refund_pubkey(wallet1: Wallet, wallet2:
async def test_p2pk_locktime_with_second_refund_pubkey(
wallet1: Wallet, wallet2: Wallet
):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() # receiver side
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side
@@ -212,7 +219,8 @@ async def test_p2pk_locktime_with_second_refund_pubkey(
@pytest.mark.asyncio
async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2
@@ -232,7 +240,8 @@ async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2
@@ -254,7 +263,8 @@ async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Walle
@pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2
@@ -273,7 +283,8 @@ async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wal
@pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2
@@ -296,7 +307,8 @@ async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wal
@pytest.mark.asyncio
async def test_p2pk_multisig_with_duplicate_publickey(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test
secret_lock = await wallet1.create_p2pk_lock(
@@ -312,7 +324,8 @@ async def test_p2pk_multisig_with_duplicate_publickey(wallet1: Wallet, wallet2:
async def test_p2pk_multisig_with_wrong_first_private_key(
wallet1: Wallet, wallet2: Wallet
):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
wrong_pubklic_key = PrivateKey().pubkey

View File

@@ -56,14 +56,16 @@ async def wallet2(mint):
@pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio
async def test_p2sh(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
_ = await wallet1.create_p2sh_address_and_store() # receiver side
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8) # sender side
@@ -76,7 +78,8 @@ async def test_p2sh(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio
async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
wallet1_address = await wallet1.create_p2sh_address_and_store() # receiver side
secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side
_, send_proofs = await wallet1.split_to_send(