Issue #313: allow checking pending invoices (#493)

* Update deprecated datetime

* Add options to Invoices cli
With these options, we are able to return:
1) all invoices (this is the default);
2) pending invoices (paid False, out False);
3) paid invoices;
4) and unpaid invoices.

* make format

* Fix mypy error with datetime

* sort imports

* Remove unneeded unit when printing out info

* Fix wrong method doc

* Try to mint pending invoices

* make pre-commit

* Refactor --tests flag to --mint
The default will be false, i.e., if the user does not
pass in the --mint flag, it will not try to mint
the pending invoice.

---------

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
Guilherme Pereira
2024-04-03 16:51:15 +01:00
committed by GitHub
parent b8ad0e0a8f
commit 3b2f1aa6f4
9 changed files with 394 additions and 74 deletions

View File

@@ -255,9 +255,9 @@ class LedgerSpendingConditions:
# check if all secrets are P2PK # check if all secrets are P2PK
# NOTE: This is redundant, because P2PKSecret.from_secret() already checks for the kind # NOTE: This is redundant, because P2PKSecret.from_secret() already checks for the kind
# Leaving it in for explicitness # Leaving it in for explicitness
if not all([ if not all(
SecretKind(secret.kind) == SecretKind.P2PK for secret in p2pk_secrets [SecretKind(secret.kind) == SecretKind.P2PK for secret in p2pk_secrets]
]): ):
# not all secrets are P2PK # not all secrets are P2PK
return True return True

View File

@@ -3,24 +3,26 @@
import asyncio import asyncio
import os import os
import time import time
from datetime import datetime from datetime import datetime, timezone
from functools import wraps from functools import wraps
from itertools import groupby, islice from itertools import groupby, islice
from operator import itemgetter from operator import itemgetter
from os import listdir from os import listdir
from os.path import isdir, join from os.path import isdir, join
from typing import Optional
import click import click
from click import Context from click import Context
from loguru import logger from loguru import logger
from ...core.base import TokenV3, Unit from ...core.base import Invoice, TokenV3, Unit
from ...core.helpers import sum_proofs from ...core.helpers import sum_proofs
from ...core.logging import configure_logger from ...core.logging import configure_logger
from ...core.settings import settings from ...core.settings import settings
from ...nostr.client.client import NostrClient from ...nostr.client.client import NostrClient
from ...tor.tor import TorProxy from ...tor.tor import TorProxy
from ...wallet.crud import ( from ...wallet.crud import (
get_lightning_invoice,
get_lightning_invoices, get_lightning_invoices,
get_reserved_proofs, get_reserved_proofs,
get_seed_and_mnemonic, get_seed_and_mnemonic,
@@ -124,7 +126,7 @@ async def cli(ctx: Context, host: str, walletname: str, unit: str, tests: bool):
env_path = settings.env_file env_path = settings.env_file
else: else:
error_str += ( error_str += (
"Ceate a new Cashu config file here:" "Create a new Cashu config file here:"
f" {os.path.join(settings.cashu_dir, '.env')}" f" {os.path.join(settings.cashu_dir, '.env')}"
) )
env_path = os.path.join(settings.cashu_dir, ".env") env_path = os.path.join(settings.cashu_dir, ".env")
@@ -158,7 +160,6 @@ async def cli(ctx: Context, host: str, walletname: str, unit: str, tests: bool):
assert wallet, "Wallet not found." assert wallet, "Wallet not found."
ctx.obj["WALLET"] = wallet ctx.obj["WALLET"] = wallet
# await init_wallet(ctx.obj["WALLET"], load_proofs=False)
# only if a command is one of a subset that needs to specify a mint host # only if a command is one of a subset that needs to specify a mint host
# if a mint host is already specified as an argument `host`, use it # if a mint host is already specified as an argument `host`, use it
@@ -166,7 +167,7 @@ async def cli(ctx: Context, host: str, walletname: str, unit: str, tests: bool):
return return
# ------ MULTIUNIT ------- : Select a unit # ------ MULTIUNIT ------- : Select a unit
ctx.obj["WALLET"] = await get_unit_wallet(ctx) ctx.obj["WALLET"] = await get_unit_wallet(ctx)
# ------ MUTLIMINT ------- : Select a wallet # ------ MULTIMINT ------- : Select a wallet
# else: we ask the user to select one # else: we ask the user to select one
ctx.obj["WALLET"] = await get_mint_wallet( ctx.obj["WALLET"] = await get_mint_wallet(
ctx ctx
@@ -637,8 +638,8 @@ async def pending(ctx: Context, legacy, number: int, offset: int):
mint = [t.mint for t in tokenObj.token][0] mint = [t.mint for t in tokenObj.token][0]
# token_hidden_secret = await wallet.serialize_proofs(grouped_proofs) # token_hidden_secret = await wallet.serialize_proofs(grouped_proofs)
assert grouped_proofs[0].time_reserved assert grouped_proofs[0].time_reserved
reserved_date = datetime.utcfromtimestamp( reserved_date = datetime.fromtimestamp(
int(grouped_proofs[0].time_reserved) int(grouped_proofs[0].time_reserved), timezone.utc
).strftime("%Y-%m-%d %H:%M:%S") ).strftime("%Y-%m-%d %H:%M:%S")
print( print(
f"#{i} Amount:" f"#{i} Amount:"
@@ -692,39 +693,120 @@ async def locks(ctx):
return True return True
@cli.command("invoices", help="List of all pending invoices.") @cli.command("invoices", help="List of all invoices.")
@click.option(
"-op",
"--only-paid",
"paid",
default=False,
is_flag=True,
help="Show only paid invoices.",
type=bool,
)
@click.option(
"-ou",
"--only-unpaid",
"unpaid",
default=False,
is_flag=True,
help="Show only unpaid invoices.",
type=bool,
)
@click.option(
"-p",
"--pending",
"pending",
default=False,
is_flag=True,
help="Show all pending invoices",
type=bool,
)
@click.option(
"--mint",
"-m",
is_flag=True,
default=False,
help="Try to mint pending invoices",
)
@click.pass_context @click.pass_context
@coro @coro
async def invoices(ctx): async def invoices(ctx, paid: bool, unpaid: bool, pending: bool, mint: bool):
wallet: Wallet = ctx.obj["WALLET"] wallet: Wallet = ctx.obj["WALLET"]
invoices = await get_lightning_invoices(db=wallet.db)
if len(invoices): if paid and unpaid:
print("") print("You should only choose one option: either --only-paid or --only-unpaid")
print("--------------------------\n") return
for invoice in invoices:
print(f"Paid: {invoice.paid}") if mint:
print(f"Incoming: {invoice.amount > 0}") await wallet.load_mint()
print(f"Amount: {abs(invoice.amount)}")
if invoice.id: paid_arg = None
print(f"ID: {invoice.id}") if unpaid:
if invoice.preimage: paid_arg = False
print(f"Preimage: {invoice.preimage}") elif paid:
if invoice.time_created: paid_arg = True
d = datetime.utcfromtimestamp(
int(float(invoice.time_created)) invoices = await get_lightning_invoices(
).strftime("%Y-%m-%d %H:%M:%S") db=wallet.db,
print(f"Created: {d}") paid=paid_arg,
if invoice.time_paid: pending=pending or None,
d = datetime.utcfromtimestamp(int(float(invoice.time_paid))).strftime( )
"%Y-%m-%d %H:%M:%S"
) if len(invoices) == 0:
print(f"Paid: {d}")
print("")
print(f"Payment request: {invoice.bolt11}")
print("")
print("--------------------------\n")
else:
print("No invoices found.") print("No invoices found.")
return
async def _try_to_mint_pending_invoice(amount: int, id: str) -> Optional[Invoice]:
try:
await wallet.mint(amount, id)
return await get_lightning_invoice(db=wallet.db, id=id)
except Exception as e:
logger.error(f"Could not mint pending invoice [{id}]: {e}")
return None
def _print_invoice_info(invoice: Invoice):
print("\n--------------------------\n")
print(f"Amount: {abs(invoice.amount)}")
print(f"ID: {invoice.id}")
print(f"Paid: {invoice.paid}")
print(f"Incoming: {invoice.amount > 0}")
if invoice.preimage:
print(f"Preimage: {invoice.preimage}")
if invoice.time_created:
d = datetime.fromtimestamp(
int(float(invoice.time_created)), timezone.utc
).strftime("%Y-%m-%d %H:%M:%S")
print(f"Created at: {d}")
if invoice.time_paid:
d = datetime.fromtimestamp(
(int(float(invoice.time_paid))), timezone.utc
).strftime("%Y-%m-%d %H:%M:%S")
print(f"Paid at: {d}")
print(f"\nPayment request: {invoice.bolt11}")
invoices_printed_count = 0
for invoice in invoices:
is_pending_invoice = invoice.out is False and invoice.paid is False
if is_pending_invoice and mint:
# Tries to mint pending invoice
updated_invoice = await _try_to_mint_pending_invoice(
invoice.amount, invoice.id
)
# If the mint ran successfully and we are querying for pending or unpaid invoices, do not print it
if pending or unpaid:
continue
# Otherwise, print the invoice with updated values
if updated_invoice:
invoice = updated_invoice
_print_invoice_info(invoice)
invoices_printed_count += 1
if invoices_printed_count == 0:
print("No invoices found.")
else:
print("\n--------------------------\n")
@cli.command("wallets", help="List of all available wallets.") @cli.command("wallets", help="List of all available wallets.")

View File

@@ -67,10 +67,12 @@ async def get_reserved_proofs(
db: Database, db: Database,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
) -> List[Proof]: ) -> List[Proof]:
rows = await (conn or db).fetchall(""" rows = await (conn or db).fetchall(
"""
SELECT * from proofs SELECT * from proofs
WHERE reserved WHERE reserved
""") """
)
return [Proof.from_dict(dict(r)) for r in rows] return [Proof.from_dict(dict(r)) for r in rows]
@@ -279,15 +281,22 @@ async def get_lightning_invoice(
async def get_lightning_invoices( async def get_lightning_invoices(
db: Database, db: Database,
paid: Optional[bool] = None, paid: Optional[bool] = None,
pending: Optional[bool] = None,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
) -> List[Invoice]: ) -> List[Invoice]:
clauses: List[Any] = [] clauses: List[Any] = []
values: List[Any] = [] values: List[Any] = []
if paid is not None: if paid is not None and not pending:
clauses.append("paid = ?") clauses.append("paid = ?")
values.append(paid) values.append(paid)
if pending:
clauses.append("paid = ?")
values.append(False)
clauses.append("out = ?")
values.append(False)
where = "" where = ""
if clauses: if clauses:
where = f"WHERE {' AND '.join(clauses)}" where = f"WHERE {' AND '.join(clauses)}"

View File

@@ -2,17 +2,20 @@ from ..core.db import Connection, Database
async def m000_create_migrations_table(conn: Connection): async def m000_create_migrations_table(conn: Connection):
await conn.execute(""" await conn.execute(
"""
CREATE TABLE IF NOT EXISTS dbversions ( CREATE TABLE IF NOT EXISTS dbversions (
db TEXT PRIMARY KEY, db TEXT PRIMARY KEY,
version INT NOT NULL version INT NOT NULL
) )
""") """
)
async def m001_initial(db: Database): async def m001_initial(db: Database):
async with db.connect() as conn: async with db.connect() as conn:
await conn.execute(f""" await conn.execute(
f"""
CREATE TABLE IF NOT EXISTS proofs ( CREATE TABLE IF NOT EXISTS proofs (
amount {db.big_int} NOT NULL, amount {db.big_int} NOT NULL,
C TEXT NOT NULL, C TEXT NOT NULL,
@@ -21,9 +24,11 @@ async def m001_initial(db: Database):
UNIQUE (secret) UNIQUE (secret)
); );
""") """
)
await conn.execute(f""" await conn.execute(
f"""
CREATE TABLE IF NOT EXISTS proofs_used ( CREATE TABLE IF NOT EXISTS proofs_used (
amount {db.big_int} NOT NULL, amount {db.big_int} NOT NULL,
C TEXT NOT NULL, C TEXT NOT NULL,
@@ -32,25 +37,30 @@ async def m001_initial(db: Database):
UNIQUE (secret) UNIQUE (secret)
); );
""") """
)
await conn.execute(""" await conn.execute(
"""
CREATE VIEW IF NOT EXISTS balance AS CREATE VIEW IF NOT EXISTS balance AS
SELECT COALESCE(SUM(s), 0) AS balance FROM ( SELECT COALESCE(SUM(s), 0) AS balance FROM (
SELECT SUM(amount) AS s SELECT SUM(amount) AS s
FROM proofs FROM proofs
WHERE amount > 0 WHERE amount > 0
); );
""") """
)
await conn.execute(""" await conn.execute(
"""
CREATE VIEW IF NOT EXISTS balance_used AS CREATE VIEW IF NOT EXISTS balance_used AS
SELECT COALESCE(SUM(s), 0) AS used FROM ( SELECT COALESCE(SUM(s), 0) AS used FROM (
SELECT SUM(amount) AS s SELECT SUM(amount) AS s
FROM proofs_used FROM proofs_used
WHERE amount > 0 WHERE amount > 0
); );
""") """
)
async def m002_add_proofs_reserved(db: Database): async def m002_add_proofs_reserved(db: Database):
@@ -96,7 +106,8 @@ async def m005_wallet_keysets(db: Database):
Stores mint keysets from different mints and epochs. Stores mint keysets from different mints and epochs.
""" """
async with db.connect() as conn: async with db.connect() as conn:
await conn.execute(f""" await conn.execute(
f"""
CREATE TABLE IF NOT EXISTS keysets ( CREATE TABLE IF NOT EXISTS keysets (
id TEXT, id TEXT,
mint_url TEXT, mint_url TEXT,
@@ -108,7 +119,8 @@ async def m005_wallet_keysets(db: Database):
UNIQUE (id, mint_url) UNIQUE (id, mint_url)
); );
""") """
)
await conn.execute("ALTER TABLE proofs ADD COLUMN id TEXT") await conn.execute("ALTER TABLE proofs ADD COLUMN id TEXT")
await conn.execute("ALTER TABLE proofs_used ADD COLUMN id TEXT") await conn.execute("ALTER TABLE proofs_used ADD COLUMN id TEXT")
@@ -119,7 +131,8 @@ async def m006_invoices(db: Database):
Stores Lightning invoices. Stores Lightning invoices.
""" """
async with db.connect() as conn: async with db.connect() as conn:
await conn.execute(f""" await conn.execute(
f"""
CREATE TABLE IF NOT EXISTS invoices ( CREATE TABLE IF NOT EXISTS invoices (
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
pr TEXT NOT NULL, pr TEXT NOT NULL,
@@ -132,7 +145,8 @@ async def m006_invoices(db: Database):
UNIQUE (hash) UNIQUE (hash)
); );
""") """
)
async def m007_nostr(db: Database): async def m007_nostr(db: Database):
@@ -140,12 +154,14 @@ async def m007_nostr(db: Database):
Stores timestamps of nostr operations. Stores timestamps of nostr operations.
""" """
async with db.connect() as conn: async with db.connect() as conn:
await conn.execute(""" await conn.execute(
"""
CREATE TABLE IF NOT EXISTS nostr ( CREATE TABLE IF NOT EXISTS nostr (
type TEXT NOT NULL, type TEXT NOT NULL,
last TIMESTAMP DEFAULT NULL last TIMESTAMP DEFAULT NULL
) )
""") """
)
await conn.execute( await conn.execute(
""" """
INSERT INTO nostr INSERT INTO nostr
@@ -172,14 +188,16 @@ async def m009_privatekey_and_determinstic_key_derivation(db: Database):
await conn.execute("ALTER TABLE keysets ADD COLUMN counter INTEGER DEFAULT 0") await conn.execute("ALTER TABLE keysets ADD COLUMN counter INTEGER DEFAULT 0")
await conn.execute("ALTER TABLE proofs ADD COLUMN derivation_path TEXT") await conn.execute("ALTER TABLE proofs ADD COLUMN derivation_path TEXT")
await conn.execute("ALTER TABLE proofs_used ADD COLUMN derivation_path TEXT") await conn.execute("ALTER TABLE proofs_used ADD COLUMN derivation_path TEXT")
await conn.execute(""" await conn.execute(
"""
CREATE TABLE IF NOT EXISTS seed ( CREATE TABLE IF NOT EXISTS seed (
seed TEXT NOT NULL, seed TEXT NOT NULL,
mnemonic TEXT NOT NULL, mnemonic TEXT NOT NULL,
UNIQUE (seed, mnemonic) UNIQUE (seed, mnemonic)
); );
""") """
)
# await conn.execute("INSERT INTO secret_derivation (counter) VALUES (0)") # await conn.execute("INSERT INTO secret_derivation (counter) VALUES (0)")

View File

@@ -133,9 +133,12 @@ class WalletP2PK(SupportsPrivateKey, SupportsDb):
return outputs return outputs
# if any of the proofs provided require SIG_ALL, we must provide it # if any of the proofs provided require SIG_ALL, we must provide it
if any([ if any(
P2PKSecret.deserialize(p.secret).sigflag == SigFlags.SIG_ALL for p in proofs [
]): P2PKSecret.deserialize(p.secret).sigflag == SigFlags.SIG_ALL
for p in proofs
]
):
outputs = await self.add_p2pk_witnesses_to_outputs(outputs) outputs = await self.add_p2pk_witnesses_to_outputs(outputs)
return outputs return outputs
@@ -181,9 +184,9 @@ class WalletP2PK(SupportsPrivateKey, SupportsDb):
return proofs return proofs
logger.debug("Spending conditions detected.") logger.debug("Spending conditions detected.")
# P2PK signatures # P2PK signatures
if all([ if all(
Secret.deserialize(p.secret).kind == SecretKind.P2PK.value for p in proofs [Secret.deserialize(p.secret).kind == SecretKind.P2PK.value for p in proofs]
]): ):
logger.debug("P2PK redemption detected.") logger.debug("P2PK redemption detected.")
proofs = await self.add_p2pk_witnesses_to_proofs(proofs) proofs = await self.add_p2pk_witnesses_to_proofs(proofs)

View File

@@ -1488,7 +1488,7 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
Args: Args:
proofs (List[Proof]): Which proofs to delete proofs (List[Proof]): Which proofs to delete
check_spendable (bool, optional): Asks the mint to check whether proofs are already spent before deleting them. Defaults to True. check_spendable (bool, optional): Asks the mint to check whether proofs are already spent before deleting them. Defaults to False.
Returns: Returns:
List[Proof]: List of proofs that are still spendable. List[Proof]: List of proofs that are still spendable.

View File

@@ -4,7 +4,7 @@ from httpx import Response
from cashu.core.base import Amount, MeltQuote, Unit from cashu.core.base import Amount, MeltQuote, Unit
from cashu.core.settings import settings from cashu.core.settings import settings
from cashu.lightning.blink import MINIMUM_FEE_MSAT, BlinkWallet from cashu.lightning.blink import MINIMUM_FEE_MSAT, BlinkWallet # type: ignore
settings.mint_blink_key = "123" settings.mint_blink_key = "123"
blink = BlinkWallet(unit=Unit.sat) blink = BlinkWallet(unit=Unit.sat)

View File

@@ -28,6 +28,23 @@ def get_bolt11_and_invoice_id_from_invoice_command(output: str) -> Tuple[str, st
return invoice, invoice_id return invoice, invoice_id
def get_invoice_from_invoices_command(output: str) -> dict[str, str]:
splitted = output.split("\n")
removed_empty_and_hiphens = [
value for value in splitted if value and not value.startswith("-----")
]
dict_output = {
f"{value.split(': ')[0]}": value.split(": ")[1]
for value in removed_empty_and_hiphens
}
return dict_output
async def reset_invoices(wallet: Wallet):
await wallet.db.execute("DELETE FROM invoices")
async def init_wallet(): async def init_wallet():
settings.debug = False settings.debug = False
wallet = await Wallet.with_db( wallet = await Wallet.with_db(
@@ -158,6 +175,197 @@ def test_invoice_with_split(mint, cli_prefix):
wallet = asyncio.run(init_wallet()) wallet = asyncio.run(init_wallet())
assert wallet.proof_amounts.count(1) >= 10 assert wallet.proof_amounts.count(1) >= 10
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
def test_invoices_with_minting(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
invoice = asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--mint"],
)
# assert
print("INVOICES --mint")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." not in result.output
assert "ID" in result.output
assert "Paid" in result.output
assert get_invoice_from_invoices_command(result.output)["ID"] == invoice.id
assert get_invoice_from_invoices_command(result.output)["Paid"] == "True"
def test_invoices_without_minting(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
invoice = asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices"],
)
# assert
print("INVOICES")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." not in result.output
assert "ID" in result.output
assert "Paid" in result.output
assert get_invoice_from_invoices_command(result.output)["ID"] == invoice.id
assert get_invoice_from_invoices_command(result.output)["Paid"] == str(invoice.paid)
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
def test_invoices_with_onlypaid_option(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--only-paid", "--mint"],
)
# assert
print("INVOICES --only-paid --mint")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." in result.output
def test_invoices_with_onlypaid_option_without_minting(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--only-paid"],
)
# assert
print("INVOICES --only-paid")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." in result.output
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
def test_invoices_with_onlyunpaid_option(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--only-unpaid", "--mint"],
)
# assert
print("INVOICES --only-unpaid --mint")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." in result.output
def test_invoices_with_onlyunpaid_option_without_minting(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
invoice = asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--only-unpaid"],
)
# assert
print("INVOICES --only-unpaid")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." not in result.output
assert "ID" in result.output
assert "Paid" in result.output
assert get_invoice_from_invoices_command(result.output)["ID"] == invoice.id
assert get_invoice_from_invoices_command(result.output)["Paid"] == str(invoice.paid)
def test_invoices_with_both_onlypaid_and_onlyunpaid_options(cli_prefix):
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--only-paid", "--only-unpaid"],
)
assert result.exception is None
print("INVOICES --only-paid --only-unpaid")
assert result.exit_code == 0
assert (
"You should only choose one option: either --only-paid or --only-unpaid"
in result.output
)
@pytest.mark.skipif(not is_fake, reason="only on fakewallet")
def test_invoices_with_pending_option(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--pending", "--mint"],
)
# assert
print("INVOICES --pending --mint")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." in result.output
def test_invoices_with_pending_option_without_minting(cli_prefix):
# arrange
wallet1 = asyncio.run(init_wallet())
asyncio.run(reset_invoices(wallet=wallet1))
invoice = asyncio.run(wallet1.request_mint(64))
# act
runner = CliRunner()
result = runner.invoke(
cli,
[*cli_prefix, "invoices", "--pending"],
)
# assert
print("INVOICES --pending")
assert result.exception is None
assert result.exit_code == 0
assert "No invoices found." not in result.output
assert "ID" in result.output
assert "Paid" in result.output
assert get_invoice_from_invoices_command(result.output)["ID"] == invoice.id
assert get_invoice_from_invoices_command(result.output)["Paid"] == str(invoice.paid)
def test_wallets(cli_prefix): def test_wallets(cli_prefix):
runner = CliRunner() runner = CliRunner()

View File

@@ -231,9 +231,9 @@ async def test_p2pk_locktime_with_second_refund_pubkey(
secret_lock = await wallet1.create_p2pk_lock( secret_lock = await wallet1.create_p2pk_lock(
garbage_pubkey.serialize().hex(), # create lock to unspendable pubkey garbage_pubkey.serialize().hex(), # create lock to unspendable pubkey
locktime_seconds=2, # locktime locktime_seconds=2, # locktime
tags=Tags([ tags=Tags(
["refund", pubkey_wallet2, pubkey_wallet1] [["refund", pubkey_wallet2, pubkey_wallet1]]
]), # multiple refund pubkeys ), # multiple refund pubkeys
) # sender side ) # sender side
_, send_proofs = await wallet1.split_to_send( _, send_proofs = await wallet1.split_to_send(
wallet1.proofs, 8, secret_lock=secret_lock wallet1.proofs, 8, secret_lock=secret_lock
@@ -388,9 +388,9 @@ async def test_p2pk_multisig_with_wrong_first_private_key(
def test_tags(): def test_tags():
tags = Tags([ tags = Tags(
["key1", "value1"], ["key2", "value2", "value2_1"], ["key2", "value3"] [["key1", "value1"], ["key2", "value2", "value2_1"], ["key2", "value3"]]
]) )
assert tags.get_tag("key1") == "value1" assert tags.get_tag("key1") == "value1"
assert tags["key1"] == "value1" assert tags["key1"] == "value1"
assert tags.get_tag("key2") == "value2" assert tags.get_tag("key2") == "value2"