diff --git a/cashu/core/base.py b/cashu/core/base.py index 3860e622..b26f953e 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -22,8 +22,8 @@ class SecretKind: class SigFlags: - SIG_INPUTS = ( - "SIG_INPUTS" # require signatures only on the inputs (default signature flag) + SIG_INPUTS = ( # require signatures only on the inputs (default signature flag) + "SIG_INPUTS" ) SIG_ALL = "SIG_ALL" # require signatures on inputs and outputs @@ -161,18 +161,20 @@ class Proof(BaseModel): id: Union[ None, str - ] = "" # NOTE: None for backwards compatibility for old clients that do not include the keyset id < 0.3 + ] = ( # NOTE: None for backwards compatibility for old clients that do not include the keyset id < 0.3 + "" + ) amount: int = 0 secret: str = "" # secret or message to be blinded and signed C: str = "" # signature on secret, unblinded by wallet p2pksigs: Union[List[str], None] = [] # P2PK signature p2shscript: Union[P2SHScript, None] = None # P2SH spending condition - reserved: Union[ - None, bool - ] = False # whether this proof is reserved for sending, used for coin management in the wallet - send_id: Union[ - None, str - ] = "" # unique ID of send attempt, used for grouping pending tokens in the wallet + reserved: Union[None, bool] = ( + False # whether this proof is reserved for sending, used for coin management in the wallet + ) + send_id: Union[None, str] = ( + "" # unique ID of send attempt, used for grouping pending tokens in the wallet + ) time_created: Union[None, str] = "" time_reserved: Union[None, str] = "" derivation_path: Union[None, str] = "" # derivation path of the proof @@ -338,9 +340,9 @@ class CheckSpendableRequest(BaseModel): class CheckSpendableResponse(BaseModel): spendable: List[bool] - pending: Optional[ - List[bool] - ] = None # TODO: Uncomment when all mints are updated to 0.12.3 and support /check + pending: Optional[List[bool]] = ( + None # TODO: Uncomment when all mints are updated to 0.12.3 and support /check + ) # with pending tokens (kept for backwards compatibility of new wallets with old mints) @@ -421,9 +423,11 @@ def deserialize(serialized: str): return cls( id=row["id"], - public_keys=deserialize(str(row["public_keys"])) - if dict(row).get("public_keys") - else {}, + public_keys=( + deserialize(str(row["public_keys"])) + if dict(row).get("public_keys") + else {} + ), mint_url=row["mint_url"], valid_from=row["valid_from"], valid_to=row["valid_to"], @@ -489,7 +493,8 @@ def generate_keys(self, seed): self.id = derive_keyset_id(self.public_keys) # type: ignore if backwards_compatibility_pre_0_12: logger.warning( - f"WARNING: Using weak key derivation for keyset {self.id} (backwards compatibility < 0.12)" + f"WARNING: Using weak key derivation for keyset {self.id} (backwards" + " compatibility < 0.12)" ) diff --git a/cashu/core/bolt11.py b/cashu/core/bolt11.py index 962581d7..3c59fbb5 100644 --- a/cashu/core/bolt11.py +++ b/cashu/core/bolt11.py @@ -184,7 +184,6 @@ def lnencode(addr, privkey): tags_set = set() for k, v in addr.tags: - # BOLT #11: # # A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields, diff --git a/cashu/core/db.py b/cashu/core/db.py index 0af326a2..60873862 100644 --- a/cashu/core/db.py +++ b/cashu/core/db.py @@ -117,9 +117,9 @@ def _parse_timestamp(value, _): psycopg2.extensions.new_type( # type: ignore (1082, 1083, 1266), "DATE2INT", - lambda value, curs: time.mktime(value.timetuple()) - if value is not None - else None, + lambda value, curs: ( + time.mktime(value.timetuple()) if value is not None else None + ), ) ) diff --git a/cashu/core/migrations.py b/cashu/core/migrations.py index 37a697c3..ce3e2bfe 100644 --- a/cashu/core/migrations.py +++ b/cashu/core/migrations.py @@ -37,11 +37,13 @@ async def run_migration(db, migrations_module): exists = None if conn.type == SQLITE: exists = await conn.fetchone( - f"SELECT * FROM sqlite_master WHERE type='table' AND name='{table_with_schema(db, 'dbversions')}'" + "SELECT * FROM sqlite_master WHERE type='table' AND" + f" name='{table_with_schema(db, 'dbversions')}'" ) elif conn.type in {POSTGRES, COCKROACH}: exists = await conn.fetchone( - f"SELECT * FROM information_schema.tables WHERE table_name = '{table_with_schema(db, 'dbversions')}'" + "SELECT * FROM information_schema.tables WHERE table_name =" + f" '{table_with_schema(db, 'dbversions')}'" ) if not exists: diff --git a/cashu/core/script.py b/cashu/core/script.py index ff471d14..0fc2a8ea 100644 --- a/cashu/core/script.py +++ b/cashu/core/script.py @@ -134,7 +134,8 @@ def verify_bitcoin_script(txin_redeemScript_b64, txin_signature_b64): txin_redeemScript_b64 = base64.urlsafe_b64encode(txin_redeemScript).decode() txin_signature_b64 = base64.urlsafe_b64encode(txin_signature).decode() print( - f"Carol to Bob:\nscript: {txin_redeemScript.__repr__()}\nscript: {txin_redeemScript_b64}\nsignature: {txin_signature_b64}\n" + f"Carol to Bob:\nscript: {txin_redeemScript.__repr__()}\nscript:" + f" {txin_redeemScript_b64}\nsignature: {txin_signature_b64}\n" ) print("") # --------- @@ -155,7 +156,8 @@ def verify_bitcoin_script(txin_redeemScript_b64, txin_signature_b64): tx, _ = step1_bob_carol_create_tx(txin_p2sh_address) print( - f"Bob verifies:\nscript: {txin_redeemScript_b64}\nsignature: {txin_signature_b64}\n" + f"Bob verifies:\nscript: {txin_redeemScript_b64}\nsignature:" + f" {txin_signature_b64}\n" ) script_valid = step3_bob_verify_script(txin_signature, txin_redeemScript, tx) # MINT redeems tokens and stores P2SH:txin_p2sh_address diff --git a/cashu/mint/app.py b/cashu/mint/app.py index 3b4ef326..412cd907 100644 --- a/cashu/mint/app.py +++ b/cashu/mint/app.py @@ -36,11 +36,16 @@ def configure_logger() -> None: class Formatter: def __init__(self): self.padding = 0 - self.minimal_fmt: str = "{time:YYYY-MM-DD HH:mm:ss.SS} | {level} | {message}\n" + self.minimal_fmt: str = ( + "{time:YYYY-MM-DD HH:mm:ss.SS} |" + " {level} | {message}\n" + ) if settings.debug: self.fmt: str = ( - "{time:YYYY-MM-DD HH:mm:ss.SS} | {level: <4} | {name}:" - "{function}:{line} | {message}\n" + "{time:YYYY-MM-DD HH:mm:ss.SS} | {level:" + " <4} |" + " {name}:{function}:{line}" + " | {message}\n" ) else: self.fmt: str = self.minimal_fmt diff --git a/cashu/mint/crud.py b/cashu/mint/crud.py index c7eaffcc..d15ab222 100644 --- a/cashu/mint/crud.py +++ b/cashu/mint/crud.py @@ -90,11 +90,9 @@ async def get_proofs_used( db: Database, conn: Optional[Connection] = None, ): - rows = await (conn or db).fetchall( - f""" + rows = await (conn or db).fetchall(f""" SELECT secret from {table_with_schema(db, 'proofs_used')} - """ - ) + """) return [row[0] for row in rows] @@ -123,11 +121,9 @@ async def get_proofs_pending( db: Database, conn: Optional[Connection] = None, ): - rows = await (conn or db).fetchall( - f""" + rows = await (conn or db).fetchall(f""" SELECT * from {table_with_schema(db, 'proofs_pending')} - """ - ) + """) return [Proof(**r) for r in rows] diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 1544c110..8f972d58 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -276,9 +276,10 @@ def _verify_input_spending_conditions(self, proof: Proof) -> bool: if not valid: raise TransactionError("script invalid.") # check if secret commits to script address - assert secret.data == str( - txin_p2sh_address - ), f"secret does not contain correct P2SH address: {secret.data} is not {txin_p2sh_address}." + assert secret.data == str(txin_p2sh_address), ( + f"secret does not contain correct P2SH address: {secret.data} is not" + f" {txin_p2sh_address}." + ) return True # P2PK @@ -310,9 +311,10 @@ def _verify_input_spending_conditions(self, proof: Proof) -> bool: assert n_sigs_required > 0, "n_sigs must be positive." # check if enough signatures are present - assert ( - len(proof.p2pksigs) >= n_sigs_required - ), f"not enough signatures provided: {len(proof.p2pksigs)} < {n_sigs_required}." + assert len(proof.p2pksigs) >= n_sigs_required, ( + f"not enough signatures provided: {len(proof.p2pksigs)} <" + f" {n_sigs_required}." + ) n_valid_sigs_per_output = 0 # loop over all signatures in output @@ -327,20 +329,24 @@ def _verify_input_spending_conditions(self, proof: Proof) -> bool: ): n_valid_sigs_per_output += 1 logger.trace( - f"p2pk signature on input is valid: {input_sig} on {pubkey}." + f"p2pk signature on input is valid: {input_sig} on" + f" {pubkey}." ) continue else: logger.trace( - f"p2pk signature on input is invalid: {input_sig} on {pubkey}." + f"p2pk signature on input is invalid: {input_sig} on" + f" {pubkey}." ) # check if we have enough valid signatures assert n_valid_sigs_per_output, "no valid signature provided for input." - assert ( - n_valid_sigs_per_output >= n_sigs_required - ), f"signature threshold not met. {n_valid_sigs_per_output} < {n_sigs_required}." + assert n_valid_sigs_per_output >= n_sigs_required, ( + f"signature threshold not met. {n_valid_sigs_per_output} <" + f" {n_sigs_required}." + ) logger.trace( - f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures found." + f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures" + " found." ) logger.trace(proof.p2pksigs) @@ -424,11 +430,13 @@ def _verify_output_spending_conditions( ): n_valid_sigs_per_output += 1 assert n_valid_sigs_per_output, "no valid signature provided for output." - assert ( - n_valid_sigs_per_output >= n_sigs_required - ), f"signature threshold not met. {n_valid_sigs_per_output} < {n_sigs_required}." + assert n_valid_sigs_per_output >= n_sigs_required, ( + f"signature threshold not met. {n_valid_sigs_per_output} <" + f" {n_sigs_required}." + ) logger.trace( - f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures found." + f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures" + " found." ) logger.trace(output.p2pksigs) logger.trace("p2pk signatures on output is valid.") @@ -492,7 +500,8 @@ async def _request_lightning_invoice(self, amount: int): Tuple[str, str]: Bolt11 invoice and payment hash (for lookup) """ logger.trace( - f"_request_lightning_invoice: Requesting Lightning invoice for {amount} satoshis." + "_request_lightning_invoice: Requesting Lightning invoice for" + f" {amount} satoshis." ) error, balance = await self.lightning.status() logger.trace(f"_request_lightning_invoice: Lightning wallet balance: {balance}") @@ -549,14 +558,16 @@ async def _check_lightning_invoice( try: if amount > invoice.amount: raise LightningError( - f"requested amount too high: {amount}. Invoice amount: {invoice.amount}" + 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: {status}" + f"_check_lightning_invoice: invoice {invoice.payment_hash} status:" + f" {status}" ) if status.paid: return status.paid @@ -658,7 +669,8 @@ async def _unset_proofs_pending( try: for p in proofs: logger.trace( - f"crud: _unset_proofs_pending unsetting proof {p.secret} as pending" + 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( @@ -1005,7 +1017,8 @@ async def check_fees(self, pr: str): decoded_invoice = bolt11.decode(pr) amount = math.ceil(decoded_invoice.amount_msat / 1000) logger.trace( - f"check_fees: checking lightning invoice: {decoded_invoice.payment_hash}" + "check_fees: checking lightning invoice:" + f" {decoded_invoice.payment_hash}" ) paid = await self.lightning.get_invoice_status(decoded_invoice.payment_hash) logger.trace(f"check_fees: paid: {paid}") @@ -1071,7 +1084,8 @@ async def split( # BEGIN backwards compatibility < 0.13.0 if amount is not None: logger.debug( - "Split: Client provided `amount` - backwards compatibility response pre 0.13.0" + "Split: Client provided `amount` - backwards compatibility response pre" + " 0.13.0" ) # split outputs according to amount total = sum_proofs(proofs) diff --git a/cashu/mint/migrations.py b/cashu/mint/migrations.py index 0ad57cdc..44779e62 100644 --- a/cashu/mint/migrations.py +++ b/cashu/mint/migrations.py @@ -2,19 +2,16 @@ async def m000_create_migrations_table(db: Database): - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'dbversions')} ( db TEXT PRIMARY KEY, version INT NOT NULL ) - """ - ) + """) async def m001_initial(db: Database): - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'promises')} ( amount {db.big_int} NOT NULL, B_b TEXT NOT NULL, @@ -23,11 +20,9 @@ async def m001_initial(db: Database): UNIQUE (B_b) ); - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'proofs_used')} ( amount {db.big_int} NOT NULL, C TEXT NOT NULL, @@ -36,11 +31,9 @@ async def m001_initial(db: Database): UNIQUE (secret) ); - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'invoices')} ( amount {db.big_int} NOT NULL, pr TEXT NOT NULL, @@ -50,49 +43,41 @@ async def m001_initial(db: Database): UNIQUE (hash) ); - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE VIEW {table_with_schema(db, 'balance_issued')} AS SELECT COALESCE(SUM(s), 0) AS balance FROM ( SELECT SUM(amount) FROM {table_with_schema(db, 'promises')} WHERE amount > 0 ) AS s; - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE VIEW {table_with_schema(db, 'balance_redeemed')} AS SELECT COALESCE(SUM(s), 0) AS balance FROM ( SELECT SUM(amount) FROM {table_with_schema(db, 'proofs_used')} WHERE amount > 0 ) AS s; - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE VIEW {table_with_schema(db, 'balance')} AS SELECT s_issued - s_used FROM ( SELECT bi.balance AS s_issued, bu.balance AS s_used FROM {table_with_schema(db, 'balance_issued')} bi CROSS JOIN {table_with_schema(db, 'balance_redeemed')} bu ) AS balance; - """ - ) + """) async def m003_mint_keysets(db: Database): """ Stores mint keysets from different mints and epochs. """ - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'keysets')} ( id TEXT NOT NULL, derivation_path TEXT, @@ -104,10 +89,8 @@ async def m003_mint_keysets(db: Database): UNIQUE (derivation_path) ); - """ - ) - await db.execute( - f""" + """) + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'mint_pubkeys')} ( id TEXT NOT NULL, amount INTEGER NOT NULL, @@ -116,8 +99,7 @@ async def m003_mint_keysets(db: Database): UNIQUE (id, pubkey) ); - """ - ) + """) async def m004_keysets_add_version(db: Database): @@ -133,8 +115,7 @@ async def m005_pending_proofs_table(db: Database) -> None: """ Store pending proofs. """ - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'proofs_pending')} ( amount INTEGER NOT NULL, C TEXT NOT NULL, @@ -143,8 +124,7 @@ async def m005_pending_proofs_table(db: Database) -> None: UNIQUE (secret) ); - """ - ) + """) async def m006_invoices_add_payment_hash(db: Database): diff --git a/cashu/mint/router.py b/cashu/mint/router.py index 2fbb8336..3ede2c6b 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -59,7 +59,10 @@ async def info() -> GetInfoResponse: "/keys", name="Mint public keys", summary="Get the public keys of the newest mint keyset", - response_description="A dictionary of all supported token values of the mint and their associated public key of the current keyset.", + response_description=( + "A dictionary of all supported token values of the mint and their associated" + " public key of the current keyset." + ), response_model=KeysResponse, ) async def keys(): @@ -74,7 +77,10 @@ async def keys(): "/keys/{idBase64Urlsafe}", name="Keyset public keys", summary="Public keys of a specific keyset", - response_description="A dictionary of all supported token values of the mint and their associated public key for a specific keyset.", + response_description=( + "A dictionary of all supported token values of the mint and their associated" + " public key for a specific keyset." + ), response_model=KeysResponse, ) async def keyset_keys(idBase64Urlsafe: str): @@ -109,7 +115,10 @@ async def keysets() -> KeysetsResponse: name="Request mint", summary="Request minting of new tokens", response_model=GetMintResponse, - response_description="A Lightning invoice to be paid and a hash to request minting of new tokens after payment.", + response_description=( + "A Lightning invoice to be paid and a hash to request minting of new tokens" + " after payment." + ), ) async def request_mint(amount: int = 0) -> GetMintResponse: """ @@ -135,7 +144,9 @@ async def request_mint(amount: int = 0) -> GetMintResponse: name="Mint tokens", summary="Mint tokens in exchange for a Bitcoin paymemt that the user has made", response_model=PostMintResponse, - response_description="A list of blinded signatures that can be used to create proofs.", + response_description=( + "A list of blinded signatures that can be used to create proofs." + ), ) async def mint( payload: PostMintRequest, @@ -163,9 +174,15 @@ async def mint( @router.post( "/melt", name="Melt tokens", - summary="Melt tokens for a Bitcoin payment that the mint will make for the user in exchange", + summary=( + "Melt tokens for a Bitcoin payment that the mint will make for the user in" + " exchange" + ), response_model=GetMeltResponse, - response_description="The state of the payment, a preimage as proof of payment, and a list of promises for change.", + response_description=( + "The state of the payment, a preimage as proof of payment, and a list of" + " promises for change." + ), ) async def melt(payload: PostMeltRequest) -> GetMeltResponse: """ @@ -225,7 +242,9 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse: name="Split", summary="Split proofs at a specified amount", response_model=Union[PostSplitResponse, PostSplitResponse_Deprecated], - response_description="A list of blinded signatures that can be used to create proofs.", + response_description=( + "A list of blinded signatures that can be used to create proofs." + ), ) async def split( payload: PostSplitRequest, @@ -259,8 +278,9 @@ async def split( else: frst_promises.insert(0, promise) # and insert at the beginning logger.trace( - f"Split into keep: {len(frst_promises)}: {sum([p.amount for p in frst_promises])} " - f"sat and send: {len(scnd_promises)}: {sum([p.amount for p in scnd_promises])} sat" + f"Split into keep: {len(frst_promises)}:" + f" {sum([p.amount for p in frst_promises])} sat and send:" + f" {len(scnd_promises)}: {sum([p.amount for p in scnd_promises])} sat" ) return PostSplitResponse_Deprecated(fst=frst_promises, snd=scnd_promises) # END backwards compatibility < 0.13 diff --git a/cashu/mint/startup.py b/cashu/mint/startup.py index f4c8de70..735f59e9 100644 --- a/cashu/mint/startup.py +++ b/cashu/mint/startup.py @@ -53,7 +53,8 @@ async def start_mint_init(): error_message, balance = await ledger.lightning.status() if error_message: logger.warning( - f"The backend for {ledger.lightning.__class__.__name__} isn't working properly: '{error_message}'", + f"The backend for {ledger.lightning.__class__.__name__} isn't working" + f" properly: '{error_message}'", RuntimeWarning, ) logger.info(f"Lightning balance: {balance} msat") diff --git a/cashu/wallet/api/router.py b/cashu/wallet/api/router.py index 7ad7e9de..3a671b6d 100644 --- a/cashu/wallet/api/router.py +++ b/cashu/wallet/api/router.py @@ -276,8 +276,9 @@ async def burn( wallet = await mint_wallet(mint) if not (all or token or force or delete) or (token and all): raise Exception( - "enter a token or use --all to burn all pending tokens, --force to check all tokens" - "or --delete with send ID to force-delete pending token from list if mint is unavailable.", + "enter a token or use --all to burn all pending tokens, --force to check" + " all tokensor --delete with send ID to force-delete pending token from" + " list if mint is unavailable.", ) if all: # check only those who are flagged as reserved diff --git a/cashu/wallet/cli/cli.py b/cashu/wallet/cli/cli.py index c3e6b377..9acf2b29 100644 --- a/cashu/wallet/cli/cli.py +++ b/cashu/wallet/cli/cli.py @@ -90,18 +90,27 @@ def wrapper(*args, **kwargs): async def cli(ctx: Context, host: str, walletname: str, tests: bool): if settings.tor and not TorProxy().check_platform(): error_str = ( - "Your settings say TOR=true but the built-in Tor bundle is not supported on your system. You have two options: Either install" - " Tor manually and set TOR=FALSE and SOCKS_HOST=localhost and SOCKS_PORT=9050 in your Cashu config (recommended). Or turn off Tor by " - "setting TOR=false (not recommended). Cashu will not work until you edit your config file accordingly." + "Your settings say TOR=true but the built-in Tor bundle is not supported on" + " your system. You have two options: Either install Tor manually and set" + " TOR=FALSE and SOCKS_HOST=localhost and SOCKS_PORT=9050 in your Cashu" + " config (recommended). Or turn off Tor by setting TOR=false (not" + " recommended). Cashu will not work until you edit your config file" + " accordingly." ) error_str += "\n\n" if settings.env_file: error_str += f"Edit your Cashu config file here: {settings.env_file}" env_path = settings.env_file else: - error_str += f"Ceate a new Cashu config file here: {os.path.join(settings.cashu_dir, '.env')}" + error_str += ( + "Ceate a new Cashu config file here:" + f" {os.path.join(settings.cashu_dir, '.env')}" + ) env_path = os.path.join(settings.cashu_dir, ".env") - error_str += f'\n\nYou can turn off Tor with this command: echo "TOR=FALSE" >> {env_path}' + error_str += ( + '\n\nYou can turn off Tor with this command: echo "TOR=FALSE" >>' + f" {env_path}" + ) raise Exception(error_str) ctx.ensure_object(dict) @@ -155,7 +164,8 @@ async def pay(ctx: Context, invoice: str, yes: bool): total_amount, fee_reserve_sat = await wallet.get_pay_amount_with_fees(invoice) if not yes: click.confirm( - f"Pay {total_amount - fee_reserve_sat} sat ({total_amount} sat with potential fees)?", + f"Pay {total_amount - fee_reserve_sat} sat ({total_amount} sat with" + " potential fees)?", abort=True, default=True, ) @@ -206,7 +216,8 @@ async def invoice(ctx: Context, amount: int, hash: str, split: int): print(f"Invoice: {invoice.pr}") print("") print( - f"If you abort this you can use this command to recheck the invoice:\ncashu invoice {amount} --hash {invoice.hash}" + "If you abort this you can use this command to recheck the" + f" invoice:\ncashu invoice {amount} --hash {invoice.hash}" ) check_until = time.time() + 5 * 60 # check for five minutes print("") @@ -232,7 +243,8 @@ async def invoice(ctx: Context, amount: int, hash: str, split: int): if not paid: print("\n") print( - "Invoice is not paid yet, stopping check. Use the command above to recheck after the invoice has been paid." + "Invoice is not paid yet, stopping check. Use the command above to" + " recheck after the invoice has been paid." ) # user paid invoice and want to check it @@ -306,7 +318,8 @@ async def balance(ctx: Context, verbose): print("") for k, v in keyset_balances.items(): print( - f"Keyset: {k} - Balance: {v['available']} sat (pending: {v['balance']-v['available']} sat)" + f"Keyset: {k} - Balance: {v['available']} sat (pending:" + f" {v['balance']-v['available']} sat)" ) print("") @@ -314,8 +327,9 @@ async def balance(ctx: Context, verbose): if verbose: print( - f"Balance: {wallet.available_balance} sat (pending: {wallet.balance-wallet.available_balance} sat) " - f"in {len([p for p in wallet.proofs if not p.reserved])} tokens" + f"Balance: {wallet.available_balance} sat (pending:" + f" {wallet.balance-wallet.available_balance} sat) in" + f" {len([p for p in wallet.proofs if not p.reserved])} tokens" ) else: print(f"Balance: {wallet.available_balance} sat") @@ -386,7 +400,7 @@ async def send_command( "-n", default=False, is_flag=True, - help="Receive tokens via nostr." "receive", + help="Receive tokens via nostr.receive", ) @click.option( "--all", "-a", default=False, is_flag=True, help="Receive all pending tokens." @@ -454,8 +468,9 @@ async def burn(ctx: Context, token: str, all: bool, force: bool, delete: str): await wallet.load_mint() if not (all or token or force or delete) or (token and all): print( - "Error: enter a token or use --all to burn all pending tokens, --force to check all tokens " - "or --delete with send ID to force-delete pending token from list if mint is unavailable." + "Error: enter a token or use --all to burn all pending tokens, --force to" + " check all tokens or --delete with send ID to force-delete pending token" + " from list if mint is unavailable." ) return if all: @@ -527,7 +542,8 @@ async def pending(ctx: Context, legacy, number: int, offset: int): int(grouped_proofs[0].time_reserved) ).strftime("%Y-%m-%d %H:%M:%S") print( - f"#{i} Amount: {sum_proofs(grouped_proofs)} sat Time: {reserved_date} ID: {key} Mint: {mint}\n" + f"#{i} Amount: {sum_proofs(grouped_proofs)} sat Time:" + f" {reserved_date} ID: {key} Mint: {mint}\n" ) print(f"{token}\n") @@ -657,7 +673,8 @@ async def wallets(ctx): if w == ctx.obj["WALLET_NAME"]: active_wallet = True print( - f"Wallet: {w}\tBalance: {sum_proofs(wallet.proofs)} sat (available: " + f"Wallet: {w}\tBalance: {sum_proofs(wallet.proofs)} sat" + " (available: " f"{sum_proofs([p for p in wallet.proofs if not p.reserved])} sat){' *' if active_wallet else ''}" ) except Exception: @@ -741,12 +758,14 @@ async def restore(ctx: Context, to: int, batch: int): ret = await get_seed_and_mnemonic(wallet.db) if ret: print( - "Wallet already has a mnemonic. You can't restore an already initialized wallet." + "Wallet already has a mnemonic. You can't restore an already initialized" + " wallet." ) print("To restore a wallet, please delete the wallet directory and try again.") print("") print( - f"The wallet directory is: {os.path.join(settings.cashu_dir, ctx.obj['WALLET_NAME'])}" + "The wallet directory is:" + f" {os.path.join(settings.cashu_dir, ctx.obj['WALLET_NAME'])}" ) return # ask the user for a mnemonic but allow also no input diff --git a/cashu/wallet/cli/cli_helpers.py b/cashu/wallet/cli/cli_helpers.py index 068d2443..e7d12b21 100644 --- a/cashu/wallet/cli/cli_helpers.py +++ b/cashu/wallet/cli/cli_helpers.py @@ -78,7 +78,8 @@ async def print_mint_balances(wallet, show_mints=False): print("") for i, (k, v) in enumerate(mint_balances.items()): print( - f"Mint {i+1}: Balance: {v['available']} sat (pending: {v['balance']-v['available']} sat) URL: {k}" + f"Mint {i+1}: Balance: {v['available']} sat (pending:" + f" {v['balance']-v['available']} sat) URL: {k}" ) print("") diff --git a/cashu/wallet/crud.py b/cashu/wallet/crud.py index 887db759..e229ccf1 100644 --- a/cashu/wallet/crud.py +++ b/cashu/wallet/crud.py @@ -31,11 +31,9 @@ async def get_proofs( db: Database, conn: Optional[Connection] = None, ): - rows = await (conn or db).fetchall( - """ + rows = await (conn or db).fetchall(""" SELECT * from proofs - """ - ) + """) return [Proof(**dict(r)) for r in rows] @@ -43,12 +41,10 @@ async def get_reserved_proofs( db: Database, conn: Optional[Connection] = None, ): - rows = await (conn or db).fetchall( - """ + rows = await (conn or db).fetchall(""" SELECT * from proofs WHERE reserved - """ - ) + """) return [Proof(**r) for r in rows] diff --git a/cashu/wallet/helpers.py b/cashu/wallet/helpers.py index ac71118d..302298d7 100644 --- a/cashu/wallet/helpers.py +++ b/cashu/wallet/helpers.py @@ -195,7 +195,8 @@ async def send( send_proofs = [p] break assert send_proofs, Exception( - f"No proof with this amount found. Available amounts: {set([p.amount for p in wallet.proofs])}" + "No proof with this amount found. Available amounts:" + f" {set([p.amount for p in wallet.proofs])}" ) await wallet.set_reserved(send_proofs, reserved=True) diff --git a/cashu/wallet/migrations.py b/cashu/wallet/migrations.py index 810def7e..061b31c5 100644 --- a/cashu/wallet/migrations.py +++ b/cashu/wallet/migrations.py @@ -2,19 +2,16 @@ async def m000_create_migrations_table(db: Database): - await db.execute( - """ + await db.execute(""" CREATE TABLE IF NOT EXISTS dbversions ( db TEXT PRIMARY KEY, version INT NOT NULL ) - """ - ) + """) async def m001_initial(db: Database): - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS proofs ( amount {db.big_int} NOT NULL, C TEXT NOT NULL, @@ -23,11 +20,9 @@ async def m001_initial(db: Database): UNIQUE (secret) ); - """ - ) + """) - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS proofs_used ( amount {db.big_int} NOT NULL, C TEXT NOT NULL, @@ -36,30 +31,25 @@ async def m001_initial(db: Database): UNIQUE (secret) ); - """ - ) + """) - await db.execute( - """ + await db.execute(""" CREATE VIEW IF NOT EXISTS balance AS SELECT COALESCE(SUM(s), 0) AS balance FROM ( SELECT SUM(amount) AS s FROM proofs WHERE amount > 0 ); - """ - ) + """) - await db.execute( - """ + await db.execute(""" CREATE VIEW IF NOT EXISTS balance_used AS SELECT COALESCE(SUM(s), 0) AS used FROM ( SELECT SUM(amount) AS s FROM proofs_used WHERE amount > 0 ); - """ - ) + """) async def m002_add_proofs_reserved(db: Database): @@ -85,8 +75,7 @@ async def m004_p2sh_locks(db: Database): """ Stores P2SH addresses and unlock scripts. """ - await db.execute( - """ + await db.execute(""" CREATE TABLE IF NOT EXISTS p2sh ( address TEXT NOT NULL, script TEXT NOT NULL, @@ -96,16 +85,14 @@ async def m004_p2sh_locks(db: Database): UNIQUE (address, script, signature) ); - """ - ) + """) async def m005_wallet_keysets(db: Database): """ Stores mint keysets from different mints and epochs. """ - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS keysets ( id TEXT, mint_url TEXT, @@ -117,8 +104,7 @@ async def m005_wallet_keysets(db: Database): UNIQUE (id, mint_url) ); - """ - ) + """) await db.execute("ALTER TABLE proofs ADD COLUMN id TEXT") await db.execute("ALTER TABLE proofs_used ADD COLUMN id TEXT") @@ -128,8 +114,7 @@ async def m006_invoices(db: Database): """ Stores Lightning invoices. """ - await db.execute( - f""" + await db.execute(f""" CREATE TABLE IF NOT EXISTS invoices ( amount INTEGER NOT NULL, pr TEXT NOT NULL, @@ -142,22 +127,19 @@ async def m006_invoices(db: Database): UNIQUE (hash) ); - """ - ) + """) async def m007_nostr(db: Database): """ Stores timestamps of nostr operations. """ - await db.execute( - """ + await db.execute(""" CREATE TABLE IF NOT EXISTS nostr ( type TEXT NOT NULL, last TIMESTAMP DEFAULT NULL ) - """ - ) + """) await db.execute( """ INSERT INTO nostr @@ -182,14 +164,12 @@ async def m009_privatekey_and_determinstic_key_derivation(db: Database): await db.execute("ALTER TABLE keysets ADD COLUMN counter INTEGER DEFAULT 0") await db.execute("ALTER TABLE proofs ADD COLUMN derivation_path TEXT") await db.execute("ALTER TABLE proofs_used ADD COLUMN derivation_path TEXT") - await db.execute( - """ + await db.execute(""" CREATE TABLE IF NOT EXISTS seed ( seed TEXT NOT NULL, mnemonic TEXT NOT NULL, UNIQUE (seed, mnemonic) ); - """ - ) + """) # await db.execute("INSERT INTO secret_derivation (counter) VALUES (0)") diff --git a/cashu/wallet/nostr.py b/cashu/wallet/nostr.py index 1ea1918c..3f508b71 100644 --- a/cashu/wallet/nostr.py +++ b/cashu/wallet/nostr.py @@ -99,8 +99,9 @@ async def receive_nostr( ): if settings.nostr_private_key is None: print( - "Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in your .env file. " - "I will create a random private key for this session but I will not remember it." + "Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in" + " your .env file. I will create a random private key for this session but I" + " will not remember it." ) print("") client = NostrClient( diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index 61cd606c..8926ffa7 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -702,7 +702,8 @@ async def _init_private_key(self, from_mnemonic: Optional[str] = None) -> None: else "" ) print( - f'Generated a new mnemonic{wallet_name}. To view it, run "cashu{wallet_command_prefix_str} info --mnemonic".' + f"Generated a new mnemonic{wallet_name}. To view it, run" + f' "cashu{wallet_command_prefix_str} info --mnemonic".' ) elif from_mnemonic: # or use the one provided @@ -1425,7 +1426,8 @@ async def invalidate( if invalidated_proofs: logger.debug( - f"Invalidating {len(invalidated_proofs)} proofs worth {sum_proofs(invalidated_proofs)} sat." + f"Invalidating {len(invalidated_proofs)} proofs worth" + f" {sum_proofs(invalidated_proofs)} sat." ) async with self.db.connect() as conn: @@ -1559,7 +1561,8 @@ async def sign_p2pk_proofs(self, proofs: List[Proof]) -> List[str]: private_key = self.private_key assert private_key.pubkey logger.trace( - f"Signing with private key: {private_key.serialize()} public key: {private_key.pubkey.serialize().hex()}" + f"Signing with private key: {private_key.serialize()} public key:" + f" {private_key.pubkey.serialize().hex()}" ) for proof in proofs: logger.trace(f"Signing proof: {proof}") diff --git a/tests/test_wallet.py b/tests/test_wallet.py index a118007f..c33eee12 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -24,7 +24,9 @@ async def assert_err(f, msg: Union[str, CashuError]): error_message: str = str(exc.args[0]) if isinstance(msg, CashuError): if msg.detail not in error_message: - raise Exception(f"CashuError. Expected error: {msg.detail}, got: {error_message}") + raise Exception( + f"CashuError. Expected error: {msg.detail}, got: {error_message}" + ) return if msg not in error_message: raise Exception(f"Expected error: {msg}, got: {error_message}") @@ -183,7 +185,9 @@ async def test_split(wallet1: Wallet): @pytest.mark.asyncio async def test_split_to_send(wallet1: Wallet): await wallet1.mint(64) - keep_proofs, spendable_proofs = await wallet1.split_to_send(wallet1.proofs, 32, set_reserved=True) + keep_proofs, spendable_proofs = await wallet1.split_to_send( + wallet1.proofs, 32, set_reserved=True + ) get_spendable = await wallet1._select_proofs_to_send(wallet1.proofs, 32) assert keep_proofs == get_spendable @@ -301,7 +305,9 @@ async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet): await wallet1.mint(64) wallet1_address = await wallet1.create_p2sh_address_and_store() # receiver side secret_lock = await wallet1.create_p2sh_lock(wallet1_address) # sender side - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock) # sender side + _, send_proofs = await wallet1.split_to_send( + wallet1.proofs, 8, secret_lock + ) # sender side await assert_err(wallet2.redeem(send_proofs), "lock not found.") # wrong receiver @@ -316,7 +322,10 @@ async def test_token_state(wallet1: Wallet): @pytest.mark.asyncio async def test_bump_secret_derivation(wallet3: Wallet): - await wallet3._init_private_key("half depart obvious quality work element tank gorilla view sugar picture humble") + await wallet3._init_private_key( + "half depart obvious quality work element tank gorilla view sugar picture" + " humble" + ) secrets1, rs1, derivaion_paths1 = await wallet3.generate_n_secrets(5) secrets2, rs2, derivaion_paths2 = await wallet3.generate_secrets_from_to(0, 4) assert secrets1 == secrets2 @@ -340,7 +349,10 @@ async def test_bump_secret_derivation(wallet3: Wallet): @pytest.mark.asyncio async def test_bump_secret_derivation_two_steps(wallet3: Wallet): - await wallet3._init_private_key("half depart obvious quality work element tank gorilla view sugar picture humble") + await wallet3._init_private_key( + "half depart obvious quality work element tank gorilla view sugar picture" + " humble" + ) secrets1_1, rs1_1, derivaion_paths1 = await wallet3.generate_n_secrets(2) secrets1_2, rs1_2, derivaion_paths2 = await wallet3.generate_n_secrets(3) secrets1 = secrets1_1 + secrets1_2 @@ -352,7 +364,10 @@ async def test_bump_secret_derivation_two_steps(wallet3: Wallet): @pytest.mark.asyncio async def test_generate_secrets_from_to(wallet3: Wallet): - await wallet3._init_private_key("half depart obvious quality work element tank gorilla view sugar picture humble") + await wallet3._init_private_key( + "half depart obvious quality work element tank gorilla view sugar picture" + " humble" + ) secrets1, rs1, derivaion_paths1 = await wallet3.generate_secrets_from_to(0, 4) assert len(secrets1) == 5 secrets2, rs2, derivaion_paths2 = await wallet3.generate_secrets_from_to(2, 4) @@ -377,14 +392,20 @@ async def test_restore_wallet_after_mint(wallet3: Wallet): @pytest.mark.asyncio async def test_restore_wallet_with_invalid_mnemonic(wallet3: Wallet): await assert_err( - wallet3._init_private_key("half depart obvious quality work element tank gorilla view sugar picture picture"), + wallet3._init_private_key( + "half depart obvious quality work element tank gorilla view sugar picture" + " picture" + ), "Invalid mnemonic", ) @pytest.mark.asyncio async def test_restore_wallet_after_split_to_send(wallet3: Wallet): - await wallet3._init_private_key("half depart obvious quality work element tank gorilla view sugar picture humble") + await wallet3._init_private_key( + "half depart obvious quality work element tank gorilla view sugar picture" + " humble" + ) await reset_wallet_db(wallet3) await wallet3.mint(64) @@ -404,7 +425,9 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet): @pytest.mark.asyncio async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: Wallet): - await wallet3._init_private_key("hello rug want adapt talent together lunar method bean expose beef position") + await wallet3._init_private_key( + "hello rug want adapt talent together lunar method bean expose beef position" + ) await reset_wallet_db(wallet3) await wallet3.mint(64) @@ -440,7 +463,10 @@ def add(self, proofs: List[Proof]) -> None: @pytest.mark.asyncio async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet): - await wallet3._init_private_key("lucky broken tell exhibit shuffle tomato ethics virus rabbit spread measure text") + await wallet3._init_private_key( + "lucky broken tell exhibit shuffle tomato ethics virus rabbit spread measure" + " text" + ) await reset_wallet_db(wallet3) await wallet3.mint(64) @@ -516,7 +542,9 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value( wallet3: Wallet, ): box = ProofBox() - await wallet3._init_private_key("casual demise flight cradle feature hub link slim remember anger front asthma") + await wallet3._init_private_key( + "casual demise flight cradle feature hub link slim remember anger front asthma" + ) await reset_wallet_db(wallet3) await wallet3.mint(64)