mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-22 03:24:18 +01:00
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:
@@ -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,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -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.")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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 == "":
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user