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): async def get_lightning_invoice(*args, **kwags):
return await get_lightning_invoice(*args, **kwags) # type: ignore return await get_lightning_invoice(*args, **kwags) # type: ignore
async def get_proofs_used(*args, **kwags): async def get_secrets_used(*args, **kwags):
return await get_proofs_used(*args, **kwags) # type: ignore return await get_secrets_used(*args, **kwags) # type: ignore
async def invalidate_proof(*args, **kwags): async def invalidate_proof(*args, **kwags):
return await invalidate_proof(*args, **kwags) # type: ignore 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 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, db: Database,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
): ):

View File

@@ -24,7 +24,6 @@ from ..core.errors import (
KeysetNotFoundError, KeysetNotFoundError,
LightningError, LightningError,
NotAllowedError, NotAllowedError,
TokenAlreadySpentError,
TransactionError, TransactionError,
) )
from ..core.helpers import fee_reserve, sum_proofs from ..core.helpers import fee_reserve, sum_proofs
@@ -50,7 +49,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
derivation_path="", derivation_path="",
crud=LedgerCrud, crud=LedgerCrud,
): ):
self.proofs_used: Set[str] = set() self.secrets_used: Set[str] = set()
self.master_key = seed self.master_key = seed
self.derivation_path = derivation_path self.derivation_path = derivation_path
@@ -60,12 +59,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
self.pubkey = derive_pubkey(self.master_key) self.pubkey = derive_pubkey(self.master_key)
self.keysets = MintKeysets([]) self.keysets = MintKeysets([])
async def load_used_proofs(self): # ------- KEYS -------
"""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)
async def load_keyset(self, derivation_path, autosave=True) -> MintKeyset: 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. """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 # load the current keyset
self.keyset = await self.load_keyset(self.derivation_path, autosave) self.keyset = await self.load_keyset(self.derivation_path, autosave)
async def _generate_promises( def get_keyset(self, keyset_id: Optional[str] = None):
self, B_s: List[BlindedMessage], keyset: Optional[MintKeyset] = None """Returns a dictionary of hex public keys of a specific keyset for each supported amount"""
) -> list[BlindedSignature]: if keyset_id and keyset_id not in self.keysets.keysets:
"""Generates promises that sum to the given amount. 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: # ------- LIGHTNING -------
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
async def _request_lightning_invoice(self, amount: int): async def _request_lightning_invoice(self, amount: int):
"""Generate a Lightning invoice using the funding source backend. """Generate a Lightning invoice using the funding source backend.
@@ -266,50 +198,26 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
Returns: Returns:
bool: True if invoice has been paid, else False 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( invoice: Union[Invoice, None] = await self.crud.get_lightning_invoice(
hash=hash, db=self.db, conn=conn hash=hash, db=self.db, conn=conn
) )
logger.trace(f"crud: _check_lightning_invoice: invoice: {invoice}")
if invoice is None: if invoice is None:
raise LightningError("invoice not found.") raise LightningError("invoice not found.")
if invoice.issued: if invoice.issued:
raise LightningError("tokens already issued for this invoice.") 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." assert invoice.payment_hash, "invoice has no payment hash."
status = await self.lightning.get_invoice_status(invoice.payment_hash)
# set this invoice as issued logger.trace(
logger.trace(f"crud: setting invoice {invoice.payment_hash} as issued") f"_check_lightning_invoice: invoice {invoice.payment_hash} status: {status}"
await self.crud.update_lightning_invoice(
hash=hash, issued=True, db=self.db, conn=conn
) )
logger.trace(f"crud: invoice {invoice.payment_hash} set as issued") if not status.paid:
raise InvoiceNotPaidError()
try: return status.paid
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
async def _pay_lightning_invoice(self, invoice: str, fee_limit_msat: int): async def _pay_lightning_invoice(self, invoice: str, fee_limit_msat: int):
"""Pays a Lightning invoice via the funding source backend. """Pays a Lightning invoice via the funding source backend.
@@ -324,9 +232,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
Returns: Returns:
Tuple[bool, string, int]: Returns payment status, preimage of invoice, paid fees (in Millisatoshi) 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() error, balance = await self.lightning.status()
logger.trace(f"_pay_lightning_invoice: Lightning wallet balance: {balance}")
if error: if error:
raise LightningError(f"Lightning wallet not responding: {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 fee_msat = abs(fee_msat) if fee_msat else fee_msat
return ok, preimage, fee_msat return ok, preimage, fee_msat
# ------- ECASH -------
async def _invalidate_proofs(self, proofs: List[Proof]): async def _invalidate_proofs(self, proofs: List[Proof]):
"""Adds secrets of proofs to the list of known secrets and stores them in the db. """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. 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. proofs (List[Proof]): Proofs to add to known secret table.
""" """
# Mark proofs as used and prepare new promises # Mark proofs as used and prepare new promises
proof_msgs = set([p.secret for p in proofs]) secrets = set([p.secret for p in proofs])
self.proofs_used |= proof_msgs self.secrets_used |= secrets
# store in db # store in db
logger.trace("crud: storing proofs")
for p in proofs: for p in proofs:
await self.crud.invalidate_proof(proof=p, db=self.db) 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( async def _generate_change_promises(
self, self,
@@ -490,14 +325,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
else: else:
return [] return []
# Public methods # ------- TRANSACTIONS -------
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()}
async def request_mint(self, amount: int): async def request_mint(self, amount: int):
"""Returns Lightning invoice and stores it in the db. """Returns Lightning invoice and stores it in the db.
@@ -561,8 +389,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
List[BlindedSignature]: Signatures on the outputs. List[BlindedSignature]: Signatures on the outputs.
""" """
logger.trace("called mint") logger.trace("called mint")
amounts = [b.amount for b in B_s] amount_outputs = sum([b.amount for b in B_s])
amount = sum(amounts)
if settings.lightning: if settings.lightning:
if not hash: if not hash:
@@ -571,15 +398,17 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
self.locks.get(hash) or asyncio.Lock() self.locks.get(hash) or asyncio.Lock()
) # create a new lock if it doesn't exist ) # create a new lock if it doesn't exist
async with self.locks[hash]: async with self.locks[hash]:
# will raise an exception if the invoice is not paid or tokens are already issued # will raise an exception if the invoice is not paid or tokens are
await self._check_lightning_invoice(amount, hash) # 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] del self.locks[hash]
for amount in amounts: self._verify_outputs(B_s)
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}."
)
promises = await self._generate_promises(B_s, keyset) promises = await self._generate_promises(B_s, keyset)
logger.trace("generated promises") logger.trace("generated promises")
@@ -615,49 +444,45 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
raise NotAllowedError( raise NotAllowedError(
f"Maximum melt amount is {settings.mint_max_peg_out} sat." 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 # verify overspending attempt
assert ( assert total_provided >= invoice_amount + fees_sat, TransactionError(
total_provided >= invoice_amount + fees_msat / 1000 "provided proofs not enough for Lightning payment. Provided:"
), TransactionError("provided proofs not enough for Lightning payment.") 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 # 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: if settings.lightning:
logger.trace("paying lightning invoice") logger.trace(f"paying lightning invoice {invoice}")
status, preimage, fee_msat = await self._pay_lightning_invoice( status, preimage, fee_msat = await self._pay_lightning_invoice(
invoice, fees_msat invoice, fees_sat * 1000
) )
logger.trace("paid lightning invoice") logger.trace("paid lightning invoice")
else: else:
status, preimage, fee_msat = True, "preimage", 0 status, preimage, fee_msat = True, "preimage", 0
logger.trace( logger.debug(
f"status: {status}, preimage: {preimage}, fee_msat: {fee_msat}" f"Melt status: {status}: preimage: {preimage}, fee_msat: {fee_msat}"
) )
if status: if not 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")
raise LightningError("Lightning payment unsuccessful.") 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: except Exception as e:
logger.trace(f"exception: {e}") logger.trace(f"exception: {e}")
raise e raise e
@@ -667,28 +492,7 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
return status, preimage, return_promises return status, preimage, return_promises
async def check_proof_state( async def get_melt_fees(self, pr: str) -> int:
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):
"""Returns the fee reserve (in sat) that a wallet must add to its proofs """Returns the fee reserve (in sat) that a wallet must add to its proofs
in order to pay a Lightning invoice. 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 id does not exist (not internal), it returns paid = None
if settings.lightning: if settings.lightning:
decoded_invoice = bolt11.decode(pr) decoded_invoice = bolt11.decode(pr)
amount = math.ceil(decoded_invoice.amount_msat / 1000) amount_msat = decoded_invoice.amount_msat
logger.trace( logger.trace(
"check_fees: checking lightning invoice:" "get_melt_fees: checking lightning invoice:"
f" {decoded_invoice.payment_hash}" f" {decoded_invoice.payment_hash}"
) )
paid = await self.lightning.get_invoice_status(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 internal = paid.paid is False
else: else:
amount = 0 amount_msat = 0
internal = True internal = True
fees_msat = fee_reserve(amount * 1000, internal) fees_msat = fee_reserve(amount_msat, internal)
fee_sat = math.ceil(fees_msat / 1000) fee_sat = math.ceil(fees_msat / 1000)
return fee_sat return fee_sat
@@ -743,21 +547,11 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
logger.trace("split called") logger.trace("split called")
await self._set_proofs_pending(proofs) await self._set_proofs_pending(proofs)
total_amount = sum_proofs(proofs)
try: 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 # 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 # Mark proofs as used and prepare new promises
await self._invalidate_proofs(proofs) await self._invalidate_proofs(proofs)
except Exception as e: except Exception as e:
logger.trace(f"split failed: {e}") logger.trace(f"split failed: {e}")
raise e raise e
@@ -815,3 +609,152 @@ class Ledger(LedgerVerification, LedgerSpendingConditions):
return_outputs.append(output) return_outputs.append(output)
logger.trace(f"promise found: {promise}") logger.trace(f"promise found: {promise}")
return return_outputs, promises 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). This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu).
""" """
logger.trace(f"> POST /checkfees: {payload}") 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}") logger.trace(f"< POST /checkfees: {fees_sat}")
return CheckFeesResponse(fee=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 from loguru import logger
@@ -15,6 +15,7 @@ from ..core.errors import (
NoSecretInProofsError, NoSecretInProofsError,
NotAllowedError, NotAllowedError,
SecretTooLongError, SecretTooLongError,
TokenAlreadySpentError,
TransactionError, TransactionError,
) )
from ..core.settings import settings from ..core.settings import settings
@@ -27,8 +28,9 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):
keyset: MintKeyset keyset: MintKeyset
keysets: MintKeysets 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 self, proofs: List[Proof], outputs: Optional[List[BlindedMessage]] = None
): ):
"""Checks all proofs and outputs for validity. """Checks all proofs and outputs for validity.
@@ -45,34 +47,51 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):
Exception: BDHKE verification failed. Exception: BDHKE verification failed.
""" """
# Verify inputs # 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 # Verify secret criteria
if not all([self._verify_secret_criteria(p) for p in proofs]): if not all([self._verify_secret_criteria(p) for p in proofs]):
raise TransactionError("secrets do not match criteria.") raise TransactionError("secrets do not match criteria.")
# verify that only unique proofs were used # verify that only unique proofs were used
if not self._verify_no_duplicate_proofs(proofs): if not self._verify_no_duplicate_proofs(proofs):
raise TransactionError("duplicate 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 # Verify ecash signatures
if not all([self._verify_proof_bdhke(p) for p in proofs]): if not all([self._verify_proof_bdhke(p) for p in proofs]):
raise TransactionError("could not verify 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: if not outputs:
return return
# Verify outputs # Verify outputs
self._verify_outputs(outputs)
# verify that only unique outputs were used # Verify inputs and outputs together
if not self._verify_no_duplicate_outputs(outputs):
raise TransactionError("duplicate promises.")
if not self._verify_input_output_amounts(proofs, outputs): if not self._verify_input_output_amounts(proofs, outputs):
raise TransactionError("input amounts less than output.") raise TransactionError("input amounts less than output.")
# Verify output spending conditions # Verify output spending conditions
if outputs and not self._verify_output_spending_conditions(proofs, outputs): if outputs and not self._verify_output_spending_conditions(proofs, outputs):
raise TransactionError("validation of output spending conditions failed.") 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]: def _verify_secret_criteria(self, proof: Proof) -> Literal[True]:
"""Verifies that a secret is present and is not too long (DOS prevention).""" """Verifies that a secret is present and is not too long (DOS prevention)."""
if proof.secret is None or proof.secret == "": 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 Tuple[List[str], List[PrivateKey], List[str]]: Secrets, blinding factors, derivation paths
""" """
if n < 1:
return [], [], []
secret_counters_start = await bump_secret_derivation( secret_counters_start = await bump_secret_derivation(
db=self.db, keyset_id=self.keyset_id, by=n, skip=skip_bump 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 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 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. 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: if secret_lock:
logger.debug(f"Spending conditions: {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_host = "0.0.0.0"
settings.mint_listen_port = SERVER_PORT settings.mint_listen_port = SERVER_PORT
settings.mint_url = SERVER_ENDPOINT settings.mint_url = SERVER_ENDPOINT
settings.lightning = False settings.lightning = True
settings.tor = False settings.tor = False
settings.mint_lightning_backend = "FakeWallet" settings.mint_lightning_backend = "FakeWallet"
settings.mint_database = "./test_data/test_mint" settings.mint_database = "./test_data/test_mint"

View File

@@ -66,13 +66,14 @@ async def test_get_keyset(ledger: Ledger):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_mint(ledger: Ledger): async def test_mint(ledger: Ledger):
invoice, payment_hash = await ledger.request_mint(8)
blinded_messages_mock = [ blinded_messages_mock = [
BlindedMessage( BlindedMessage(
amount=8, amount=8,
B_="02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239", B_="02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239",
) )
] ]
promises = await ledger.mint(blinded_messages_mock) promises = await ledger.mint(blinded_messages_mock, hash=payment_hash)
assert len(promises) assert len(promises)
assert promises[0].amount == 8 assert promises[0].amount == 8
assert ( assert (
@@ -83,6 +84,7 @@ async def test_mint(ledger: Ledger):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_mint_invalid_blinded_message(ledger: Ledger): async def test_mint_invalid_blinded_message(ledger: Ledger):
invoice, payment_hash = await ledger.request_mint(8)
blinded_messages_mock_invalid_key = [ blinded_messages_mock_invalid_key = [
BlindedMessage( BlindedMessage(
amount=8, amount=8,
@@ -90,7 +92,8 @@ async def test_mint_invalid_blinded_message(ledger: Ledger):
) )
] ]
await assert_err( 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 @pytest.mark.asyncio
async def test_mint(wallet1: Wallet): 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 assert wallet1.balance == 64
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_mint_amounts(wallet1: Wallet): async def test_mint_amounts(wallet1: Wallet):
"""Mint predefined amounts""" """Mint predefined amounts"""
invoice = await wallet1.request_mint(64)
amts = [1, 1, 1, 2, 2, 4, 16] 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.balance == 27
assert wallet1.proof_amounts == amts assert wallet1.proof_amounts == amts
@@ -171,7 +173,8 @@ async def test_mint_amounts_wrong_order(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_split(wallet1: Wallet): 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 assert wallet1.balance == 64
p1, p2 = await wallet1.split(wallet1.proofs, 20) p1, p2 = await wallet1.split(wallet1.proofs, 20)
assert wallet1.balance == 64 assert wallet1.balance == 64
@@ -185,7 +188,8 @@ async def test_split(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_split_to_send(wallet1: Wallet): 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( keep_proofs, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True wallet1.proofs, 32, set_reserved=True
) )
@@ -199,7 +203,8 @@ async def test_split_to_send(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_split_more_than_balance(wallet1: Wallet): 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( await assert_err(
wallet1.split(wallet1.proofs, 128), wallet1.split(wallet1.proofs, 128),
# "Mint Error: inputs do not have same amount as outputs", # "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 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 @pytest.mark.asyncio
async def test_split_to_send_more_than_balance(wallet1: Wallet): 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( await assert_err(
wallet1.split_to_send(wallet1.proofs, 128, set_reserved=True), wallet1.split_to_send(wallet1.proofs, 128, set_reserved=True),
"balance too low.", "balance too low.",
@@ -221,7 +244,8 @@ async def test_split_to_send_more_than_balance(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_double_spend(wallet1: Wallet): 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 wallet1.split(wallet1.proofs, 20)
await assert_err( await assert_err(
wallet1.split(doublespend, 20), wallet1.split(doublespend, 20),
@@ -233,7 +257,8 @@ async def test_double_spend(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_duplicate_proofs_double_spent(wallet1: Wallet): 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( await assert_err(
wallet1.split(wallet1.proofs + doublespend, 20), wallet1.split(wallet1.proofs + doublespend, 20),
"Mint Error: proofs already pending.", "Mint Error: proofs already pending.",
@@ -244,7 +269,8 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet): 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( _, spendable_proofs = await wallet1.split_to_send(
wallet1.proofs, 32, set_reserved=True wallet1.proofs, 32, set_reserved=True
) )
@@ -261,7 +287,8 @@ async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_invalidate_unspent_proofs(wallet1: Wallet): async def test_invalidate_unspent_proofs(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet. Should not work!""" """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) await wallet1.invalidate(wallet1.proofs)
assert wallet1.balance == 64 assert wallet1.balance == 64
@@ -269,14 +296,16 @@ async def test_invalidate_unspent_proofs(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_invalidate_unspent_proofs_without_checking(wallet1: Wallet): async def test_invalidate_unspent_proofs_without_checking(wallet1: Wallet):
"""Try to invalidate proofs that have not been spent yet but force no check.""" """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) await wallet1.invalidate(wallet1.proofs, check_spendable=False)
assert wallet1.balance == 0 assert wallet1.balance == 0
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_split_invalid_amount(wallet1: Wallet): 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( await assert_err(
wallet1.split(wallet1.proofs, -1), wallet1.split(wallet1.proofs, -1),
"amount must be positive.", "amount must be positive.",
@@ -285,14 +314,16 @@ async def test_split_invalid_amount(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet): 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() pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True) PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2sh(wallet1: Wallet, wallet2: Wallet): 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 _ = await wallet1.create_p2sh_address_and_store() # receiver side
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8) # sender 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 @pytest.mark.asyncio
async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet): 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 wallet1_address = await wallet1.create_p2sh_address_and_store() # receiver side
secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side
_, send_proofs = await wallet1.split_to_send( _, 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 @pytest.mark.asyncio
async def test_token_state(wallet1: Wallet): 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 assert wallet1.balance == 64
resp = await wallet1.check_proof_state(wallet1.proofs) resp = await wallet1.check_proof_state(wallet1.proofs)
assert resp.dict()["spendable"] 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" "half depart obvious quality work element tank gorilla view sugar picture"
" humble" " humble"
) )
secrets1, rs1, derivaion_paths1 = await wallet3.generate_n_secrets(5) secrets1, rs1, derivation_paths1 = await wallet3.generate_n_secrets(5)
secrets2, rs2, derivaion_paths2 = await wallet3.generate_secrets_from_to(0, 4) secrets2, rs2, derivation_paths2 = await wallet3.generate_secrets_from_to(0, 4)
assert secrets1 == secrets2 assert secrets1 == secrets2
assert [r.private_key for r in rs1] == [r.private_key for r in rs2] 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 == [ assert secrets1 == [
"9bfb12704297fe90983907d122838940755fcce370ce51e9e00a4275a347c3fe", "9bfb12704297fe90983907d122838940755fcce370ce51e9e00a4275a347c3fe",
"dbc5e05f2b1f24ec0e2ab6e8312d5e13f57ada52594d4caf429a697d9c742490", "dbc5e05f2b1f24ec0e2ab6e8312d5e13f57ada52594d4caf429a697d9c742490",
@@ -341,7 +374,7 @@ async def test_bump_secret_derivation(wallet3: Wallet):
"652d08c804bd2c5f2c1f3e3d8895860397df394b30473753227d766affd15e89", "652d08c804bd2c5f2c1f3e3d8895860397df394b30473753227d766affd15e89",
"654e5997f8a20402f7487296b6f7e463315dd52fc6f6cc5a4e35c7f6ccac77e0", "654e5997f8a20402f7487296b6f7e463315dd52fc6f6cc5a4e35c7f6ccac77e0",
] ]
assert derivaion_paths1 == [ assert derivation_paths1 == [
"m/129372'/0'/2004500376'/0'", "m/129372'/0'/2004500376'/0'",
"m/129372'/0'/2004500376'/1'", "m/129372'/0'/2004500376'/1'",
"m/129372'/0'/2004500376'/2'", "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" "half depart obvious quality work element tank gorilla view sugar picture"
" humble" " humble"
) )
secrets1_1, rs1_1, derivaion_paths1 = await wallet3.generate_n_secrets(2) secrets1_1, rs1_1, derivation_paths1 = await wallet3.generate_n_secrets(2)
secrets1_2, rs1_2, derivaion_paths2 = await wallet3.generate_n_secrets(3) secrets1_2, rs1_2, derivation_paths2 = await wallet3.generate_n_secrets(3)
secrets1 = secrets1_1 + secrets1_2 secrets1 = secrets1_1 + secrets1_2
rs1 = rs1_1 + rs1_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 secrets1 == secrets2
assert [r.private_key for r in rs1] == [r.private_key for r in rs2] 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" "half depart obvious quality work element tank gorilla view sugar picture"
" humble" " 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 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 len(secrets2) == 3
assert secrets1[2:] == secrets2 assert secrets1[2:] == secrets2
assert [r.private_key for r in rs1[2:]] == [r.private_key for r in rs2] 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 @pytest.mark.asyncio
async def test_restore_wallet_after_mint(wallet3: Wallet): async def test_restore_wallet_after_mint(wallet3: Wallet):
await reset_wallet_db(wallet3) 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 assert wallet3.balance == 64
await reset_wallet_db(wallet3) await reset_wallet_db(wallet3)
await wallet3.load_proofs() 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 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 assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore _, 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" "hello rug want adapt talent together lunar method bean expose beef position"
) )
await reset_wallet_db(wallet3) await reset_wallet_db(wallet3)
invoice = await wallet3.request_mint(64)
await wallet3.mint(64) await wallet3.mint(64, hash=invoice.hash)
assert wallet3.balance == 64 assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore _, 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 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 assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore _, 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() wallet3.private_key = PrivateKey()
await reset_wallet_db(wallet3) 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) box.add(wallet3.proofs)
assert wallet3.balance == 2 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 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) box.add(wallet3.proofs)
assert wallet3.balance == 64 assert wallet3.balance == 64

View File

@@ -58,7 +58,8 @@ async def wallet2(mint):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_htlc_secret(wallet1: Wallet): 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 = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage) secret = await wallet1.create_htlc_lock(preimage=preimage)
@@ -67,7 +68,8 @@ async def test_create_htlc_secret(wallet1: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_htlc_split(wallet1: Wallet, wallet2: Wallet): 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 = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage) secret = await wallet1.create_htlc_lock(preimage=preimage)
@@ -79,7 +81,8 @@ async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet): 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 = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage) 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 @pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet): 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 = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage[:-1] + "1") 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 @pytest.mark.asyncio
async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet): 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" preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # 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 @pytest.mark.asyncio
async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet): 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" preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # 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 @pytest.mark.asyncio
async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wallet): 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" preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # 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( async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
wallet1: Wallet, wallet2: Wallet wallet1: Wallet, wallet2: Wallet
): ):
await wallet1.mint(64) invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000" preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.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( async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature(
wallet1: Wallet, wallet2: Wallet wallet1: Wallet, wallet2: Wallet
): ):
await wallet1.mint(64) invoice = await wallet1.request_mint(64)
await wallet1.mint(64, hash=invoice.hash)
preimage = "00000000000000000000000000000000" preimage = "00000000000000000000000000000000"
pubkey_wallet1 = await wallet1.create_p2pk_pubkey() pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()

View File

@@ -59,14 +59,16 @@ async def wallet2(mint):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet): 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() pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True) PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2pk(wallet1: Wallet, wallet2: Wallet): 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() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test # p2pk test
secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # sender side 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 @pytest.mark.asyncio
async def test_p2pk_receive_with_wrong_private_key(wallet1: Wallet, wallet2: Wallet): 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 pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side # sender side
secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # 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( async def test_p2pk_short_locktime_receive_with_wrong_private_key(
wallet1: Wallet, wallet2: Wallet 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 pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side # sender side
secret_lock = await wallet1.create_p2pk_lock( 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 @pytest.mark.asyncio
async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet): 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 pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side # sender side
garbage_pubkey = PrivateKey().pubkey garbage_pubkey = PrivateKey().pubkey
@@ -148,7 +153,8 @@ async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2pk_locktime_with_wrong_refund_pubkey(wallet1: Wallet, wallet2: Wallet): 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 await wallet2.create_p2pk_pubkey() # receiver side
# sender side # sender side
garbage_pubkey = PrivateKey().pubkey 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( async def test_p2pk_locktime_with_second_refund_pubkey(
wallet1: Wallet, wallet2: Wallet 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_wallet1 = await wallet1.create_p2pk_pubkey() # receiver side
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side
# sender side # sender side
@@ -212,7 +219,8 @@ async def test_p2pk_locktime_with_second_refund_pubkey(
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet): 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_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2 assert pubkey_wallet1 != pubkey_wallet2
@@ -232,7 +240,8 @@ async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Wallet): 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_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2 assert pubkey_wallet1 != pubkey_wallet2
@@ -254,7 +263,8 @@ async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Walle
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wallet): 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_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2 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 @pytest.mark.asyncio
async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wallet): 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_wallet1 = await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
assert pubkey_wallet1 != pubkey_wallet2 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 @pytest.mark.asyncio
async def test_p2pk_multisig_with_duplicate_publickey(wallet1: Wallet, wallet2: Wallet): 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() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
# p2pk test # p2pk test
secret_lock = await wallet1.create_p2pk_lock( 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( async def test_p2pk_multisig_with_wrong_first_private_key(
wallet1: Wallet, wallet2: Wallet 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() await wallet1.create_p2pk_pubkey()
pubkey_wallet2 = await wallet2.create_p2pk_pubkey() pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
wrong_pubklic_key = PrivateKey().pubkey wrong_pubklic_key = PrivateKey().pubkey

View File

@@ -56,14 +56,16 @@ async def wallet2(mint):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_p2pk_pubkey(wallet1: Wallet): 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() pubkey = await wallet1.create_p2pk_pubkey()
PublicKey(bytes.fromhex(pubkey), raw=True) PublicKey(bytes.fromhex(pubkey), raw=True)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_p2sh(wallet1: Wallet, wallet2: Wallet): 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 _ = await wallet1.create_p2sh_address_and_store() # receiver side
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8) # sender 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 @pytest.mark.asyncio
async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet): 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 wallet1_address = await wallet1.create_p2sh_address_and_store() # receiver side
secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side
_, send_proofs = await wallet1.split_to_send( _, send_proofs = await wallet1.split_to_send(