[DEV] add ruff and remove isort and flake (#300)

* [DEV] add ruff and remove isort and flake
- precommit
- workflow
- Makefile

updated black

* configure black to use default line-length

* reformat to 88 chars line-length

* fix ugly comments
This commit is contained in:
dni ⚡
2023-08-24 09:47:47 +02:00
committed by GitHub
parent 0a5beb75a2
commit 88393fa4c4
26 changed files with 357 additions and 333 deletions

View File

@@ -1,8 +0,0 @@
[flake8]
max-line-length = 150
exclude = cashu/nostr, cashu/core/bolt11.py
ignore =
# E203 whitespace before ':' black does not like it
E203,
# W503: line break before binary operator
W503,

View File

@@ -24,9 +24,7 @@ jobs:
run: poetry install
- name: Check black
run: make black-check
- name: Check isort
run: make isort-check
linting:
mypy:
runs-on: ubuntu-latest
strategy:
matrix:
@@ -49,5 +47,8 @@ jobs:
run: yes | poetry run mypy cashu --install-types || true
- name: Run mypy
run: poetry run mypy cashu --ignore-missing
- name: Run flake8
run: poetry run flake8
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1

View File

@@ -1,5 +1,4 @@
exclude: '^cashu/nostr/.*'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
@@ -14,16 +13,11 @@ repos:
- id: mixed-line-ending
- id: check-case-conflict
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.283
hooks:
- id: isort
args: ['--profile', 'black']
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
entry: poetry run flake8
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]

View File

@@ -1,8 +1,8 @@
isort:
poetry run isort --profile black . --skip cashu/nostr
ruff:
poetry run ruff check . --fix
isort-check:
poetry run isort --profile black --check-only . --skip cashu/nostr
ruff-check:
poetry run ruff check .
black:
poetry run black . --exclude cashu/nostr
@@ -13,12 +13,9 @@ black-check:
mypy:
poetry run mypy cashu --ignore-missing
flake8:
poetry run flake8 cashu
format: black ruff
format: isort black
check: isort-check black-check flake8 mypy
check: black-check ruff-check mypy
clean:
rm -r cashu.egg-info/ || true

View File

@@ -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
@@ -159,20 +159,17 @@ class Proof(BaseModel):
Value token
"""
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
id: Union[None, str] = ""
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
# whether this proof is reserved for sending, used for coin management in the wallet
reserved: Union[None, bool] = False
# unique ID of send attempt, used for grouping pending tokens in the wallet
send_id: Union[None, str] = ""
time_created: Union[None, str] = ""
time_reserved: Union[None, str] = ""
derivation_path: Union[None, str] = "" # derivation path of the proof
@@ -338,9 +335,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 +418,11 @@ class WalletKeyset:
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 +488,8 @@ class MintKeyset:
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)"
)

View File

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

View File

@@ -117,9 +117,9 @@ class Database(Compat):
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
),
)
)

View File

@@ -37,11 +37,13 @@ async def migrate_databases(db: Database, 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:

View File

@@ -134,7 +134,8 @@ if __name__ == "__main__":
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 @@ if __name__ == "__main__":
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

View File

@@ -36,11 +36,16 @@ def create_app(config_object="core.settings") -> FastAPI:
class Formatter:
def __init__(self):
self.padding = 0
self.minimal_fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>\n"
self.minimal_fmt: str = (
"<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> |"
" <level>{level}</level> | <level>{message}</level>\n"
)
if settings.debug:
self.fmt: str = (
"<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <4}</level> | <cyan>{name}</cyan>:<cyan>"
"{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>\n"
"<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level:"
" <4}</level> |"
" <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan>"
" | <level>{message}</level>\n"
)
else:
self.fmt: str = self.minimal_fmt

View File

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

View File

@@ -276,9 +276,10 @@ class Ledger:
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 @@ class Ledger:
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 @@ class Ledger:
):
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 @@ class Ledger:
):
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 @@ class Ledger:
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 @@ class Ledger:
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 @@ class Ledger:
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 @@ class Ledger:
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 @@ class Ledger:
# 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)

View File

@@ -2,19 +2,16 @@ from ..core.db import Database, table_with_schema
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):

View File

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

View File

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

View File

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

View File

@@ -90,18 +90,27 @@ def coro(f):
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

View File

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

View File

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

View File

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

View File

@@ -2,19 +2,16 @@ from ..core.db import Database
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)")

View File

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

View File

@@ -702,7 +702,8 @@ class Wallet(LedgerAPI):
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 @@ class Wallet(LedgerAPI):
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 @@ class Wallet(LedgerAPI):
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}")

135
poetry.lock generated
View File

@@ -104,31 +104,42 @@ files = [
[[package]]
name = "black"
version = "22.12.0"
version = "23.7.0"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
{file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
{file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
{file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
{file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
{file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
{file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
{file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
{file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
{file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
{file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
{file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"},
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"},
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"},
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"},
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"},
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"},
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"},
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
]
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
packaging = ">=22.0"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
[package.extras]
@@ -614,22 +625,6 @@ files = [
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
[[package]]
name = "flake8"
version = "6.0.0"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.8.1"
files = [
{file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"},
{file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"},
]
[package.dependencies]
mccabe = ">=0.7.0,<0.8.0"
pycodestyle = ">=2.10.0,<2.11.0"
pyflakes = ">=3.0.0,<3.1.0"
[[package]]
name = "h11"
version = "0.12.0"
@@ -740,23 +735,6 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "isort"
version = "5.12.0"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
files = [
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
]
[package.extras]
colors = ["colorama (>=0.4.3)"]
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]
[[package]]
name = "loguru"
version = "0.6.0"
@@ -795,17 +773,6 @@ docs = ["alabaster (==0.7.13)", "autodocsumm (==0.2.11)", "sphinx (==7.0.1)", "s
lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]]
name = "mnemonic"
version = "0.20"
@@ -1039,17 +1006,6 @@ files = [
{file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"},
]
[[package]]
name = "pycodestyle"
version = "2.10.0"
description = "Python style guide checker"
optional = false
python-versions = ">=3.6"
files = [
{file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"},
{file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"},
]
[[package]]
name = "pycparser"
version = "2.21"
@@ -1154,17 +1110,6 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pyflakes"
version = "3.0.1"
description = "passive checker of Python programs"
optional = false
python-versions = ">=3.6"
files = [
{file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"},
{file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"},
]
[[package]]
name = "pysocks"
version = "1.7.1"
@@ -1363,6 +1308,32 @@ idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
[package.extras]
idna2008 = ["idna"]
[[package]]
name = "ruff"
version = "0.0.284"
description = "An extremely fast Python linter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.0.284-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:8b949084941232e2c27f8d12c78c5a6a010927d712ecff17231ee1a8371c205b"},
{file = "ruff-0.0.284-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:a3930d66b35e4dc96197422381dff2a4e965e9278b5533e71ae8474ef202fab0"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1f7096038961d8bc3b956ee69d73826843eb5b39a5fa4ee717ed473ed69c95"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bcaf85907fc905d838f46490ee15f04031927bbea44c478394b0bfdeadc27362"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3660b85a9d84162a055f1add334623ae2d8022a84dcd605d61c30a57b436c32"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0a3218458b140ea794da72b20ea09cbe13c4c1cdb7ac35e797370354628f4c05"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2fe880cff13fffd735387efbcad54ba0ff1272bceea07f86852a33ca71276f4"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1d098ea74d0ce31478765d1f8b4fbdbba2efc532397b5c5e8e5ea0c13d7e5ae"},
{file = "ruff-0.0.284-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c79ae3308e308b94635cd57a369d1e6f146d85019da2fbc63f55da183ee29b"},
{file = "ruff-0.0.284-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f86b2b1e7033c00de45cc176cf26778650fb8804073a0495aca2f674797becbb"},
{file = "ruff-0.0.284-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e37e086f4d623c05cd45a6fe5006e77a2b37d57773aad96b7802a6b8ecf9c910"},
{file = "ruff-0.0.284-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d29dfbe314e1131aa53df213fdfea7ee874dd96ea0dd1471093d93b59498384d"},
{file = "ruff-0.0.284-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:88295fd649d0aa1f1271441df75bf06266a199497afd239fd392abcfd75acd7e"},
{file = "ruff-0.0.284-py3-none-win32.whl", hash = "sha256:735cd62fccc577032a367c31f6a9de7c1eb4c01fa9a2e60775067f44f3fc3091"},
{file = "ruff-0.0.284-py3-none-win_amd64.whl", hash = "sha256:f67ed868d79fbcc61ad0fa034fe6eed2e8d438d32abce9c04b7c4c1464b2cf8e"},
{file = "ruff-0.0.284-py3-none-win_arm64.whl", hash = "sha256:1292cfc764eeec3cde35b3a31eae3f661d86418b5e220f5d5dba1c27a6eccbb6"},
{file = "ruff-0.0.284.tar.gz", hash = "sha256:ebd3cc55cd499d326aac17a331deaea29bea206e01c08862f9b5c6e93d77a491"},
]
[[package]]
name = "secp256k1"
version = "0.14.0"
@@ -1672,4 +1643,4 @@ pgsql = ["psycopg2-binary"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8.1"
content-hash = "7bc36d84b841a4799da7a9f631d90195a569d3bf38dd5b4a7ed47a5901940d88"
content-hash = "83e8f09ebad4bca75c90591fbe0c50d0acf9d19edf6e30437e91fded95cd88c4"

View File

@@ -39,13 +39,12 @@ pgsql = ["psycopg2-binary"]
[tool.poetry.group.dev.dependencies]
mypy = "^0.971"
black = {version = "^22.8.0", allow-prereleases = true}
isort = "^5.10.1"
pytest-asyncio = "^0.19.0"
pytest-cov = "^4.0.0"
pytest = "^7.4.0"
flake8 = "^6.0.0"
pre-commit = "^3.3.3"
ruff = "^0.0.284"
black = "^23.7.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
@@ -55,3 +54,62 @@ build-backend = "poetry.core.masonry.api"
mint = "cashu.mint.main:main"
cashu = "cashu.wallet.cli.cli:cli"
wallet-test = "tests.test_wallet:test"
[tool.black]
line-length = 88
# previously experimental-string-processing = true
# this should autoformat string properly but does not work
preview = true
[tool.ruff]
# Same as Black. but black has a 10% overflow rule
line-length = 150
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
# (`I`) means isorting
select = ["E", "F", "I"]
ignore = []
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
"cashu/nostr",
"cashu/core/bolt11.py",
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.8
# target-version = "py38"
[tool.ruff.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10

View File

@@ -94,7 +94,7 @@ async def test_get_keys(wallet1: Wallet):
assert len(wallet1.keys.public_keys) == settings.max_order
keyset = await wallet1._get_keys(wallet1.url)
assert keyset.id is not None
assert type(keyset.id) == str
assert isinstance(keyset.id, str)
assert len(keyset.id) > 0
@@ -244,9 +244,7 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet):
@pytest.mark.asyncio
async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet):
await wallet1.mint(64)
_, spendable_proofs = await wallet1.split_to_send( # type: ignore
wallet1.proofs, 32, set_reserved=True
)
_, spendable_proofs = await wallet1.split_to_send(wallet1.proofs, 32, set_reserved=True) # type: ignore
await wallet2.redeem(spendable_proofs)
assert wallet2.balance == 32
@@ -325,7 +323,8 @@ 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"
"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)
@@ -351,7 +350,8 @@ 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"
"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)
@@ -365,7 +365,8 @@ 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"
"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
@@ -392,7 +393,8 @@ async def test_restore_wallet_after_mint(wallet3: Wallet):
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"
"half depart obvious quality work element tank gorilla view sugar picture"
" picture"
),
"Invalid mnemonic",
)
@@ -401,16 +403,15 @@ async def test_restore_wallet_with_invalid_mnemonic(wallet3: Wallet):
@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"
"half depart obvious quality work element tank gorilla view sugar picture"
" humble"
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 32, set_reserved=True
)
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
await reset_wallet_db(wallet3)
await wallet3.load_proofs()
@@ -432,9 +433,7 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W
await wallet3.mint(64)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 32, set_reserved=True
)
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
await wallet2.redeem(spendable_proofs)
@@ -465,16 +464,15 @@ class ProofBox:
@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"
"lucky broken tell exhibit shuffle tomato ethics virus rabbit spread measure"
" text"
)
await reset_wallet_db(wallet3)
await wallet3.mint(64)
assert wallet3.balance == 64
_, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 32, set_reserved=True
)
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 32, set_reserved=True) # type: ignore
await wallet3.redeem(spendable_proofs)
@@ -500,9 +498,7 @@ async def test_restore_wallet_after_send_twice(
box.add(wallet3.proofs)
assert wallet3.balance == 2
keep_proofs, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 1, set_reserved=True
)
keep_proofs, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 1, set_reserved=True) # type: ignore
box.add(wallet3.proofs)
assert wallet3.available_balance == 1
await wallet3.redeem(spendable_proofs)
@@ -522,9 +518,7 @@ async def test_restore_wallet_after_send_twice(
# again
_, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 1, set_reserved=True
)
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 1, set_reserved=True) # type: ignore
box.add(wallet3.proofs)
assert wallet3.available_balance == 1
@@ -557,9 +551,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
box.add(wallet3.proofs)
assert wallet3.balance == 64
keep_proofs, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 10, set_reserved=True
)
keep_proofs, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 10, set_reserved=True) # type: ignore
box.add(wallet3.proofs)
assert wallet3.available_balance == 64 - 10
@@ -579,9 +571,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value(
# again
_, spendable_proofs = await wallet3.split_to_send( # type: ignore
wallet3.proofs, 12, set_reserved=True
)
_, spendable_proofs = await wallet3.split_to_send(wallet3.proofs, 12, set_reserved=True) # type: ignore
assert wallet3.available_balance == 64 - 12
await wallet3.redeem(spendable_proofs)