diff --git a/.env.example b/.env.example index 893ed44..8929a02 100644 --- a/.env.example +++ b/.env.example @@ -42,6 +42,8 @@ MINT_DATABASE=data/mint # increment derivation path to rotate to a new keyset MINT_DERIVATION_PATH="0/0/0/0" +MINT_DATABASE=data/mint + # Lightning # Supported: LNbitsWallet, FakeWallet MINT_LIGHTNING_BACKEND=LNbitsWallet diff --git a/README.md b/README.md index e2ed341..71f33a7 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ cashu info Returns: ```bash -Version: 0.12.3 +Version: 0.13.0 Debug: False Cashu dir: /home/user/.cashu Wallet: wallet diff --git a/cashu/core/base.py b/cashu/core/base.py index 4b38c78..b724e48 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -174,6 +174,7 @@ class Proof(BaseModel): ] = "" # 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 def to_dict(self): # dictionary without the fields that don't need to be send to Carol @@ -349,6 +350,14 @@ class CheckFeesResponse(BaseModel): fee: Union[int, None] +# ------- API: RESTORE ------- + + +class PostRestoreResponse(BaseModel): + outputs: List[BlindedMessage] = [] + promises: List[BlindedSignature] = [] + + # ------- KEYSETS ------- @@ -571,7 +580,7 @@ class TokenV3(BaseModel): return list(set([p.id for p in self.get_proofs()])) @classmethod - def deserialize(cls, tokenv3_serialized: str): + def deserialize(cls, tokenv3_serialized: str) -> "TokenV3": """ Takes a TokenV3 and serializes it as "cashuA. """ @@ -583,7 +592,7 @@ class TokenV3(BaseModel): token = json.loads(base64.urlsafe_b64decode(token_base64)) return cls.parse_obj(token) - def serialize(self): + def serialize(self) -> str: """ Takes a TokenV3 and serializes it as "cashuA. """ diff --git a/cashu/core/crypto/b_dhke.py b/cashu/core/crypto/b_dhke.py index df5adc2..741bdd9 100644 --- a/cashu/core/crypto/b_dhke.py +++ b/cashu/core/crypto/b_dhke.py @@ -51,13 +51,10 @@ def hash_to_curve(message: bytes) -> PublicKey: def step1_alice( - secret_msg: str, blinding_factor: Optional[bytes] = None + secret_msg: str, blinding_factor: Optional[PrivateKey] = None ) -> tuple[PublicKey, PrivateKey]: Y: PublicKey = hash_to_curve(secret_msg.encode("utf-8")) - if blinding_factor: - r = PrivateKey(privkey=blinding_factor, raw=True) - else: - r = PrivateKey() + r = blinding_factor or PrivateKey() B_: PublicKey = Y + r.pubkey # type: ignore return B_, r diff --git a/cashu/core/migrations.py b/cashu/core/migrations.py index 4889943..61d21b5 100644 --- a/cashu/core/migrations.py +++ b/cashu/core/migrations.py @@ -1,5 +1,7 @@ import re +from loguru import logger + from ..core.db import COCKROACH, POSTGRES, SQLITE, Database, table_with_schema @@ -22,6 +24,7 @@ async def migrate_databases(db: Database, migrations_module): if match: version = int(match.group(1)) if version > current_versions.get(db_name, 0): + logger.debug(f"Migrating {db_name} db: {key}") await migrate(db) if db.schema == None: diff --git a/cashu/core/settings.py b/cashu/core/settings.py index 4fc4550..882288d 100644 --- a/cashu/core/settings.py +++ b/cashu/core/settings.py @@ -8,7 +8,7 @@ from pydantic import BaseSettings, Extra, Field env = Env() -VERSION = "0.12.3" +VERSION = "0.13.0" def find_env_file(): @@ -43,8 +43,6 @@ class CashuSettings(BaseSettings): class EnvSettings(CashuSettings): debug: bool = Field(default=False) log_level: str = Field(default="INFO") - host: str = Field(default="127.0.0.1") - port: int = Field(default=3338) cashu_dir: str = Field(default=os.path.join(str(Path.home()), ".cashu")) diff --git a/cashu/core/split.py b/cashu/core/split.py index 44b9cf5..79e75d0 100644 --- a/cashu/core/split.py +++ b/cashu/core/split.py @@ -1,8 +1,8 @@ -def amount_split(amount): +def amount_split(amount: int): """Given an amount returns a list of amounts returned e.g. 13 is [1, 4, 8].""" bits_amt = bin(amount)[::-1][:-2] rv = [] - for (pos, bit) in enumerate(bits_amt): + for pos, bit in enumerate(bits_amt): if bit == "1": rv.append(2**pos) return rv diff --git a/cashu/mint/crud.py b/cashu/mint/crud.py index 0d3116b..c1b638e 100644 --- a/cashu/mint/crud.py +++ b/cashu/mint/crud.py @@ -1,7 +1,7 @@ import time from typing import Any, List, Optional -from ..core.base import Invoice, MintKeyset, Proof +from ..core.base import BlindedSignature, Invoice, MintKeyset, Proof from ..core.db import Connection, Database, table_with_schema @@ -42,6 +42,9 @@ class LedgerCrud: async def store_promise(*args, **kwags): return await store_promise(*args, **kwags) # type: ignore + async def get_promise(*args, **kwags): + return await get_promise(*args, **kwags) # type: ignore + async def update_lightning_invoice(*args, **kwags): return await update_lightning_invoice(*args, **kwags) # type: ignore @@ -51,22 +54,39 @@ async def store_promise( amount: int, B_: str, C_: str, + id: str, conn: Optional[Connection] = None, ): await (conn or db).execute( f""" INSERT INTO {table_with_schema(db, 'promises')} - (amount, B_b, C_b) - VALUES (?, ?, ?) + (amount, B_b, C_b, id) + VALUES (?, ?, ?, ?) """, ( amount, str(B_), str(C_), + id, ), ) +async def get_promise( + db: Database, + B_: str, + conn: Optional[Connection] = None, +): + row = await (conn or db).fetchone( + f""" + SELECT * from {table_with_schema(db, 'promises')} + WHERE B_b = ? + """, + (str(B_),), + ) + return BlindedSignature(amount=row[0], C_=row[2], id=row[3]) if row else None + + async def get_proofs_used( db: Database, conn: Optional[Connection] = None, @@ -88,13 +108,14 @@ async def invalidate_proof( await (conn or db).execute( f""" INSERT INTO {table_with_schema(db, 'proofs_used')} - (amount, C, secret) - VALUES (?, ?, ?) + (amount, C, secret, id) + VALUES (?, ?, ?, ?) """, ( proof.amount, str(proof.C), str(proof.secret), + str(proof.id), ), ) diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 21af789..1511a3b 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -1,5 +1,4 @@ import asyncio -import json import math import time from typing import Dict, List, Literal, Optional, Set, Tuple, Union @@ -178,7 +177,11 @@ class Ledger: C_ = b_dhke.step2_bob(B_, private_key_amount) logger.trace(f"crud: _generate_promise storing promise for {amount}") await self.crud.store_promise( - amount=amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), db=self.db + amount=amount, + B_=B_.serialize().hex(), + C_=C_.serialize().hex(), + id=keyset.id, + db=self.db, ) logger.trace(f"crud: _generate_promise stored promise for {amount}") return BlindedSignature(id=keyset.id, amount=amount, C_=C_.serialize().hex()) @@ -804,7 +807,7 @@ class Ledger: """ logger.trace(f"called request_mint") if settings.mint_max_peg_in and amount > settings.mint_max_peg_in: - raise Exception(f"Maximum mint amount is {settings.mint_max_peg_in} sats.") + raise Exception(f"Maximum mint amount is {settings.mint_max_peg_in} sat.") if settings.mint_peg_out_only: raise Exception("Mint does not allow minting new tokens.") @@ -904,7 +907,7 @@ class Ledger: invoice_amount = math.ceil(invoice_obj.amount_msat / 1000) if settings.mint_max_peg_out and invoice_amount > settings.mint_max_peg_out: raise Exception( - f"Maximum melt amount is {settings.mint_max_peg_out} sats." + f"Maximum melt amount is {settings.mint_max_peg_out} sat." ) fees_msat = await self.check_fees(invoice) assert total_provided >= invoice_amount + fees_msat / 1000, Exception( @@ -1067,3 +1070,26 @@ class Ledger: logger.trace(f"split successful") return prom_fst, prom_snd + + async def restore( + self, outputs: List[BlindedMessage] + ) -> Tuple[List[BlindedMessage], List[BlindedSignature]]: + promises: List[BlindedSignature] = [] + return_outputs: List[BlindedMessage] = [] + async with self.db.connect() as conn: + for output in outputs: + logger.trace(f"looking for promise: {output}") + promise = await self.crud.get_promise( + B_=output.B_, db=self.db, conn=conn + ) + if promise is not None: + # BEGIN backwards compatibility mints pre `m007_proofs_and_promises_store_id` + # add keyset id to promise if not present only if the current keyset + # is the only one ever used + if not promise.id and len(self.keysets.keysets) == 1: + promise.id = self.keyset.id + # END backwards compatibility + promises.append(promise) + return_outputs.append(output) + logger.trace(f"promise found: {promise}") + return return_outputs, promises diff --git a/cashu/mint/migrations.py b/cashu/mint/migrations.py index 587d775..0ad57cd 100644 --- a/cashu/mint/migrations.py +++ b/cashu/mint/migrations.py @@ -159,3 +159,20 @@ async def m006_invoices_add_payment_hash(db: Database): await db.execute( f"UPDATE {table_with_schema(db, 'invoices')} SET payment_hash = hash" ) + + +async def m007_proofs_and_promises_store_id(db: Database): + """ + Column that remembers the payment_hash as we're using + the column hash as a random identifier now + (see https://github.com/cashubtc/nuts/pull/14). + """ + await db.execute( + f"ALTER TABLE {table_with_schema(db, 'proofs_used')} ADD COLUMN id TEXT" + ) + await db.execute( + f"ALTER TABLE {table_with_schema(db, 'proofs_pending')} ADD COLUMN id TEXT" + ) + await db.execute( + f"ALTER TABLE {table_with_schema(db, 'promises')} ADD COLUMN id TEXT" + ) diff --git a/cashu/mint/router.py b/cashu/mint/router.py index d8bde40..991aa40 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -19,6 +19,7 @@ from ..core.base import ( PostMeltRequest, PostMintRequest, PostMintResponse, + PostRestoreResponse, PostSplitRequest, PostSplitResponse, ) @@ -109,7 +110,7 @@ async def request_mint(amount: int = 0) -> Union[GetMintResponse, CashuError]: """ logger.trace(f"> GET /mint: amount={amount}") if amount > 21_000_000 * 100_000_000 or amount <= 0: - return CashuError(code=0, error="Amount must be a valid amount of sats.") + return CashuError(code=0, error="Amount must be a valid amount of sat.") if settings.mint_peg_out_only: return CashuError(code=0, error="Mint does not allow minting new tokens.") try: @@ -229,3 +230,15 @@ async def split( resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises) logger.trace(f"< POST /split: {resp}") return resp + + +@router.post( + "/restore", name="Restore", summary="Restores a blinded signature from a secret" +) +async def restore(payload: PostMintRequest) -> Union[CashuError, PostRestoreResponse]: + assert payload.outputs, Exception("no outputs provided.") + try: + outputs, promises = await ledger.restore(payload.outputs) + except Exception as exc: + return CashuError(code=0, error=str(exc)) + return PostRestoreResponse(outputs=outputs, promises=promises) diff --git a/cashu/wallet/api/responses.py b/cashu/wallet/api/responses.py index b84b7d7..931aa56 100644 --- a/cashu/wallet/api/responses.py +++ b/cashu/wallet/api/responses.py @@ -65,6 +65,10 @@ class WalletsResponse(BaseModel): wallets: Dict +class RestoreResponse(BaseModel): + balance: int + + class InfoResponse(BaseModel): version: str wallet: str diff --git a/cashu/wallet/api/router.py b/cashu/wallet/api/router.py index c12f910..d0a26c8 100644 --- a/cashu/wallet/api/router.py +++ b/cashu/wallet/api/router.py @@ -1,3 +1,4 @@ +import asyncio import os from datetime import datetime from itertools import groupby, islice @@ -29,6 +30,7 @@ from .responses import ( PayResponse, PendingResponse, ReceiveResponse, + RestoreResponse, SendResponse, SwapResponse, WalletsResponse, @@ -40,12 +42,17 @@ router: APIRouter = APIRouter() def create_wallet( url=settings.mint_url, dir=settings.cashu_dir, name=settings.wallet_name ): - return Wallet(url, os.path.join(dir, name), name=name) + return Wallet( + url=url, + db=os.path.join(dir, name), + name=name, + ) async def load_mint(wallet: Wallet, mint: Optional[str] = None): if mint: wallet = create_wallet(mint) + await init_wallet(wallet) await wallet.load_mint() return wallet @@ -390,6 +397,19 @@ async def wallets(): return WalletsResponse(wallets=result) +@router.post("/restore", name="Restore wallet", response_model=RestoreResponse) +async def restore( + to: int = Query(default=..., description="Counter to which restore the wallet"), +): + if to < 0: + raise Exception("Counter must be positive") + await wallet.load_mint() + await wallet.restore_promises(0, to) + await wallet.invalidate(wallet.proofs) + wallet.status() + return RestoreResponse(balance=wallet.available_balance) + + @router.get("/info", name="Information about Cashu wallet", response_model=InfoResponse) async def info(): if settings.nostr_private_key: diff --git a/cashu/wallet/cli/cli.py b/cashu/wallet/cli/cli.py index d0f7bf4..a582578 100644 --- a/cashu/wallet/cli/cli.py +++ b/cashu/wallet/cli/cli.py @@ -19,7 +19,12 @@ from ...core.helpers import sum_proofs from ...core.settings import settings from ...nostr.nostr.client.client import NostrClient from ...tor.tor import TorProxy -from ...wallet.crud import get_lightning_invoices, get_reserved_proofs, get_unused_locks +from ...wallet.crud import ( + get_lightning_invoices, + get_reserved_proofs, + get_seed_and_mnemonic, + get_unused_locks, +) from ...wallet.wallet import Wallet as Wallet from ..api.api_server import start_api_server from ..cli.cli_helpers import get_mint_wallet, print_mint_balances, verify_mint @@ -41,6 +46,15 @@ def run_api_server(ctx, param, daemon): ctx.exit() +# https://github.com/pallets/click/issues/85#issuecomment-503464628 +def coro(f): + @wraps(f) + def wrapper(*args, **kwargs): + return asyncio.run(f(*args, **kwargs)) + + return wrapper + + @click.group(cls=NaturalOrderGroup) @click.option( "--host", @@ -64,8 +78,16 @@ def run_api_server(ctx, param, daemon): callback=run_api_server, help="Start server for wallet REST API", ) +@click.option( + "--tests", + "-t", + is_flag=True, + default=False, + help="Run in test mode (don't ask for CLI inputs)", +) @click.pass_context -def cli(ctx: Context, host: str, walletname: str): +@coro +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." error_str += "\n\n" @@ -81,31 +103,38 @@ def cli(ctx: Context, host: str, walletname: str): ctx.ensure_object(dict) ctx.obj["HOST"] = host or settings.mint_url ctx.obj["WALLET_NAME"] = walletname - wallet = Wallet( - ctx.obj["HOST"], os.path.join(settings.cashu_dir, walletname), name=walletname - ) - ctx.obj["WALLET"] = wallet - asyncio.run(init_wallet(ctx.obj["WALLET"], load_proofs=False)) + settings.wallet_name = walletname - # MUTLIMINT: Select a wallet + db_path = os.path.join(settings.cashu_dir, walletname) + # if the command is "restore" we don't want to ask the user for a mnemonic + # otherwise it will create a mnemonic and store it in the database + if ctx.invoked_subcommand == "restore": + wallet = await Wallet.with_db( + ctx.obj["HOST"], db_path, name=walletname, skip_private_key=True + ) + else: + # # we need to run the migrations before we load the wallet for the first time + # # otherwise the wallet will not be able to generate a new private key and store it + wallet = await Wallet.with_db( + ctx.obj["HOST"], db_path, name=walletname, skip_private_key=True + ) + # now with the migrations done, we can load the wallet and generate a new mnemonic if needed + wallet = await Wallet.with_db(ctx.obj["HOST"], db_path, name=walletname) + + assert wallet, "Wallet not found." + ctx.obj["WALLET"] = wallet + # await init_wallet(ctx.obj["WALLET"], load_proofs=False) + + # ------ MUTLIMINT ------- : Select a wallet # 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 ctx.invoked_subcommand not in ["send", "invoice", "pay"] or host: return # else: we ask the user to select one - ctx.obj["WALLET"] = asyncio.run( - get_mint_wallet(ctx) + ctx.obj["WALLET"] = await get_mint_wallet( + ctx ) # select a specific wallet by CLI input - asyncio.run(init_wallet(ctx.obj["WALLET"], load_proofs=False)) - - -# https://github.com/pallets/click/issues/85#issuecomment-503464628 -def coro(f): - @wraps(f) - def wrapper(*args, **kwargs): - return asyncio.run(f(*args, **kwargs)) - - return wrapper + await init_wallet(ctx.obj["WALLET"], load_proofs=False) @cli.command("pay", help="Pay Lightning invoice.") @@ -227,7 +256,7 @@ async def swap(ctx: Context): if incoming_wallet.url == outgoing_wallet.url: raise Exception("mints for swap have to be different") - amount = int(input("Enter amount to swap in sats: ")) + amount = int(input("Enter amount to swap in sat: ")) assert amount > 0, "amount is not positive" # request invoice from incoming mint @@ -550,6 +579,8 @@ async def locks(ctx): lock_str = f"P2PK:{pubkey}" print("---- Pay to public key (P2PK) lock ----\n") print(f"Lock: {lock_str}") + print("") + print("To see more information enter: cashu lock") # P2SH locks locks = await get_unused_locks(db=wallet.db) if len(locks): @@ -561,8 +592,7 @@ async def locks(ctx): print(f"Signature: {l.signature}") print("") print(f"--------------------------\n") - else: - print("No locks found. Create one using: cashu lock") + return True @@ -616,7 +646,7 @@ async def wallets(ctx): for w in wallets: wallet = Wallet(ctx.obj["HOST"], os.path.join(settings.cashu_dir, w)) try: - await init_wallet(wallet) + await wallet.load_proofs() if wallet.proofs and len(wallet.proofs): active_wallet = False if w == ctx.obj["WALLET_NAME"]: @@ -629,12 +659,12 @@ async def wallets(ctx): @cli.command("info", help="Information about Cashu wallet.") -@click.option( - "--mint", "-m", default=False, is_flag=True, help="Fetch mint information." -) +@click.option("--mint", default=False, is_flag=True, help="Fetch mint information.") +@click.option("--mnemonic", default=False, is_flag=True, help="Show your mnemonic.") @click.pass_context @coro -async def info(ctx: Context, mint: bool): +async def info(ctx: Context, mint: bool, mnemonic: bool): + wallet: Wallet = ctx.obj["WALLET"] print(f"Version: {settings.version}") print(f"Wallet: {ctx.obj['WALLET_NAME']}") if settings.debug: @@ -657,7 +687,6 @@ async def info(ctx: Context, mint: bool): print(f"HTTP proxy: {settings.http_proxy}") print(f"Mint URL: {ctx.obj['HOST']}") if mint: - wallet: Wallet = ctx.obj["WALLET"] mint_info: dict = (await wallet._load_mint_info()).dict() print("") print("Mint information:") @@ -677,4 +706,52 @@ async def info(ctx: Context, mint: bool): if mint_info["parameter"]: print(f"Parameter: {mint_info['parameter']}") + if mnemonic: + assert wallet.mnemonic + print(f"Mnemonic: {wallet.mnemonic}") return + + +@cli.command("restore", help="Restore backups.") +@click.option( + "--batch", + "-b", + default=25, + help="Batch size. Specifies how many proofs are restored in one batch.", + type=int, +) +@click.option( + "--to", + "-t", + default=2, + help="Number of empty batches to complete the restore process.", + type=int, +) +@click.pass_context +@coro +async def restore(ctx: Context, to: int, batch: int): + wallet: Wallet = ctx.obj["WALLET"] + # check if there is already a mnemonic in the database + ret = await get_seed_and_mnemonic(wallet.db) + if ret: + print( + "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'])}" + ) + return + # ask the user for a mnemonic but allow also no input + print("Please enter your mnemonic to restore your balance.") + mnemonic = input( + "Enter mnemonic: ", + ) + if not mnemonic: + print("No mnemonic entered. Exiting.") + return + + await wallet.restore_wallet_from_mnemonic(mnemonic, to=to, batch=batch) + await wallet.load_proofs() + wallet.status() diff --git a/cashu/wallet/crud.py b/cashu/wallet/crud.py index 4d0e989..9c5b6ea 100644 --- a/cashu/wallet/crud.py +++ b/cashu/wallet/crud.py @@ -1,6 +1,6 @@ import json import time -from typing import Any, List, Optional +from typing import Any, List, Optional, Tuple from ..core.base import Invoice, KeyBase, P2SHScript, Proof, WalletKeyset from ..core.db import Connection, Database @@ -14,10 +14,17 @@ async def store_proof( await (conn or db).execute( """ INSERT INTO proofs - (id, amount, C, secret, time_created) - VALUES (?, ?, ?, ?, ?) + (id, amount, C, secret, time_created, derivation_path) + VALUES (?, ?, ?, ?, ?, ?) """, - (proof.id, proof.amount, str(proof.C), str(proof.secret), int(time.time())), + ( + proof.id, + proof.amount, + str(proof.C), + str(proof.secret), + int(time.time()), + proof.derivation_path, + ), ) @@ -62,10 +69,17 @@ async def invalidate_proof( await (conn or db).execute( """ INSERT INTO proofs_used - (amount, C, secret, time_used, id) - VALUES (?, ?, ?, ?, ?) + (amount, C, secret, time_used, id, derivation_path) + VALUES (?, ?, ?, ?, ?, ?) """, - (proof.amount, str(proof.C), str(proof.secret), int(time.time()), proof.id), + ( + proof.amount, + str(proof.C), + str(proof.secret), + int(time.time()), + proof.id, + proof.derivation_path, + ), ) @@ -329,6 +343,52 @@ async def update_lightning_invoice( ) +async def bump_secret_derivation( + db: Database, + keyset_id: str, + by: int = 1, + skip: bool = False, + conn: Optional[Connection] = None, +): + rows = await (conn or db).fetchone( + "SELECT counter from keysets WHERE id = ?", (keyset_id,) + ) + # if no counter for this keyset, create one + if not rows: + await (conn or db).execute( + "UPDATE keysets SET counter = ? WHERE id = ?", + ( + 0, + keyset_id, + ), + ) + counter = 0 + else: + counter = int(rows[0]) + + if not skip: + await (conn or db).execute( + f"UPDATE keysets SET counter = counter + {by} WHERE id = ?", + (keyset_id,), + ) + return counter + + +async def set_secret_derivation( + db: Database, + keyset_id: str, + counter: int, + conn: Optional[Connection] = None, +): + await (conn or db).execute( + "UPDATE keysets SET counter = ? WHERE id = ?", + ( + counter, + keyset_id, + ), + ) + + async def set_nostr_last_check_timestamp( db: Database, timestamp: int, @@ -351,3 +411,41 @@ async def get_nostr_last_check_timestamp( ("dm",), ) return row[0] if row else None + + +async def get_seed_and_mnemonic( + db: Database, + conn: Optional[Connection] = None, +) -> Optional[Tuple[str, str]]: + row = await (conn or db).fetchone( + f""" + SELECT seed, mnemonic from seed + """, + ) + return ( + ( + row[0], + row[1], + ) + if row + else None + ) + + +async def store_seed_and_mnemonic( + db: Database, + seed: str, + mnemonic: str, + conn: Optional[Connection] = None, +): + await (conn or db).execute( + f""" + INSERT INTO seed + (seed, mnemonic) + VALUES (?, ?) + """, + ( + seed, + mnemonic, + ), + ) diff --git a/cashu/wallet/helpers.py b/cashu/wallet/helpers.py index 9206a6d..3449014 100644 --- a/cashu/wallet/helpers.py +++ b/cashu/wallet/helpers.py @@ -1,23 +1,27 @@ import base64 import json import os -from typing import List, Optional -import click from loguru import logger from ..core.base import TokenV1, TokenV2, TokenV3, TokenV3Token +from ..core.db import Database from ..core.helpers import sum_proofs from ..core.migrations import migrate_databases from ..core.settings import settings from ..wallet import migrations -from ..wallet.crud import get_keyset, get_unused_locks -from ..wallet.wallet import Wallet as Wallet +from ..wallet.crud import get_keyset +from ..wallet.wallet import Wallet + + +async def migrate_wallet_db(db: Database): + await migrate_databases(db, migrations) async def init_wallet(wallet: Wallet, load_proofs: bool = True): """Performs migrations and loads proofs from db.""" - await migrate_databases(wallet.db, migrations) + await wallet._migrate_database() + await wallet._init_private_key() if load_proofs: await wallet.load_proofs(reload=True) @@ -31,7 +35,9 @@ async def redeem_TokenV3_multimint(wallet: Wallet, token: TokenV3): assert t.mint, Exception( "redeem_TokenV3_multimint: multimint redeem without URL" ) - mint_wallet = Wallet(t.mint, os.path.join(settings.cashu_dir, wallet.name)) + mint_wallet = await Wallet.with_db( + t.mint, os.path.join(settings.cashu_dir, wallet.name) + ) keysets = mint_wallet._get_proofs_keysets(t.proofs) logger.debug(f"Keysets in tokens: {keysets}") # loop over all keysets @@ -130,7 +136,7 @@ async def receive( assert mint_keysets, Exception("we don't know this keyset") assert mint_keysets.mint_url, Exception("we don't know this mint's URL") # now we have the URL - mint_wallet = Wallet( + mint_wallet = await Wallet.with_db( mint_keysets.mint_url, os.path.join(settings.cashu_dir, wallet.name), ) diff --git a/cashu/wallet/migrations.py b/cashu/wallet/migrations.py index d9cab1e..057ce09 100644 --- a/cashu/wallet/migrations.py +++ b/cashu/wallet/migrations.py @@ -176,3 +176,20 @@ async def m008_keysets_add_public_keys(db: Database): Stores public keys of mint in a new column of table keysets. """ await db.execute("ALTER TABLE keysets ADD COLUMN public_keys TEXT") + + +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( + """ + 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/wallet.py b/cashu/wallet/wallet.py index 4374c3f..69f2da9 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -1,5 +1,6 @@ import asyncio import base64 +import hashlib import json import math import secrets as scrts @@ -9,8 +10,11 @@ from datetime import datetime, timedelta from itertools import groupby from typing import Dict, List, Optional, Tuple, Union +import click import requests +from bip32 import BIP32 from loguru import logger +from mnemonic import Mnemonic from ..core import bolt11 as bolt11 from ..core.base import ( @@ -29,6 +33,7 @@ from ..core.base import ( PostMintRequest, PostMintResponse, PostMintResponseLegacy, + PostRestoreResponse, PostSplitRequest, Proof, Secret, @@ -46,6 +51,7 @@ from ..core.crypto import b_dhke from ..core.crypto.secp import PrivateKey, PublicKey from ..core.db import Database from ..core.helpers import calculate_number_of_blank_outputs, sum_proofs +from ..core.migrations import migrate_databases from ..core.p2pk import sign_p2pk_sign from ..core.script import ( step0_carol_checksig_redeemscrip, @@ -58,18 +64,23 @@ from ..core.split import amount_split from ..nostr.nostr.client.client import NostrClient from ..tor.tor import TorProxy from ..wallet.crud import ( + bump_secret_derivation, get_keyset, get_proofs, + get_seed_and_mnemonic, get_unused_locks, invalidate_proof, secret_used, + set_secret_derivation, store_keyset, store_lightning_invoice, store_p2sh, store_proof, + store_seed_and_mnemonic, update_lightning_invoice, update_proof_reserved, ) +from . import migrations def async_set_requests(func): @@ -104,18 +115,27 @@ def async_set_requests(func): return wrapper -class LedgerAPI: +class LedgerAPI(object): keys: WalletKeyset # holds current keys of mint keyset_id: str # holds id of current keyset public_keys: Dict[int, PublicKey] # holds public keys of mint_info: GetInfoResponse # holds info about mint tor: TorProxy - db: Database s: requests.Session + db: Database - def __init__(self, url): + def __init__(self, url: str, db: Database): self.url = url self.s = requests.Session() + self.db = db + + async def generate_n_secrets( + self, n: int = 1, skip_bump: bool = False + ) -> Tuple[List[str], List[PrivateKey], List[str]]: + return await self.generate_n_secrets(n, skip_bump) + + async def _generate_secret(self, skip_bump: bool = False) -> str: + return await self._generate_secret(skip_bump) @async_set_requests async def _init_s(self): @@ -123,12 +143,29 @@ class LedgerAPI: return def _construct_proofs( - self, promises: List[BlindedSignature], secrets: List[str], rs: List[PrivateKey] + self, + promises: List[BlindedSignature], + secrets: List[str], + rs: List[PrivateKey], + derivation_paths: List[str], ) -> List[Proof]: - """Returns proofs of promise from promises. Wants secrets and blinding factors rs.""" + """Constructs proofs from promises, secrets, rs and derivation paths. + + This method is called after the user has received blind signatures from + the mint. The results are proofs that can be used as ecash. + + Args: + promises (List[BlindedSignature]): blind signatures from mint + secrets (List[str]): secrets that were previously used to create blind messages (that turned into promises) + rs (List[PrivateKey]): blinding factors that were previously used to create blind messages (that turned into promises) + derivation_paths (List[str]): derivation paths that were used to generate secrets and blinding factors + + Returns: + List[Proof]: list of proofs that can be used as ecash + """ logger.trace(f"Constructing proofs.") proofs: List[Proof] = [] - for promise, secret, r in zip(promises, secrets, rs): + for promise, secret, r, path in zip(promises, secrets, rs, derivation_paths): logger.trace(f"Creating proof with keyset {self.keyset_id} = {promise.id}") assert ( self.keyset_id == promise.id @@ -142,22 +179,74 @@ class LedgerAPI: amount=promise.amount, C=C.serialize().hex(), secret=secret, + derivation_path=path, ) proofs.append(proof) + logger.trace( + f"Created proof: {proof}, r: {r.serialize()} out of promise {promise}" + ) + logger.trace(f"Constructed {len(proofs)} proofs.") return proofs + @staticmethod + def _construct_outputs( + amounts: List[int], secrets: List[str], rs: List[PrivateKey] = [] + ) -> Tuple[List[BlindedMessage], List[PrivateKey]]: + """Takes a list of amounts and secrets and returns outputs. + Outputs are blinded messages `outputs` and blinding factors `rs` + + Args: + amounts (List[int]): list of amounts + secrets (List[str]): list of secrets + rs (List[PrivateKey], optional): list of blinding factors. If not given, `rs` are generated in step1_alice. Defaults to []. + + Returns: + List[BlindedMessage]: list of blinded messages that can be sent to the mint + List[PrivateKey]: list of blinding factors that can be used to construct proofs after receiving blind signatures from the mint + + Raises: + AssertionError: if len(amounts) != len(secrets) + """ + assert len(amounts) == len( + secrets + ), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}" + outputs: List[BlindedMessage] = [] + + rs_ = [None] * len(amounts) if not rs else rs + rs_return: List[PrivateKey] = [] + for secret, amount, r in zip(secrets, amounts, rs_): + B_, r = b_dhke.step1_alice(secret, r or None) + rs_return.append(r) + output = BlindedMessage(amount=amount, B_=B_.serialize().hex()) + outputs.append(output) + logger.trace(f"Constructing output: {output}, r: {r.serialize()}") + + return outputs, rs_return + @staticmethod def raise_on_error(resp_dict) -> None: + """Raises an exception if the response from the mint contains an error. + + Args: + resp_dict (_type_): Response dict (previously JSON) from mint + + Raises: + Exception: if the response contains an error + """ if "error" in resp_dict: raise Exception("Mint Error: {}".format(resp_dict["error"])) - @staticmethod - def _generate_secret(randombits=128): - """Returns base64 encoded random string.""" - return scrts.token_urlsafe(randombits // 8) - async def _load_mint_keys(self, keyset_id: str = "") -> WalletKeyset: + """Loads keys from mint and stores them in the database. + + Args: + keyset_id (str, optional): keyset id to load. If given, requests keys for this keyset from the mint. If not given, requests current keyset of the mint. Defaults to "". + + Raises: + AssertionError: if mint URL is not set + AssertionError: if no keys are received from the mint + """ assert len( self.url ), "Ledger not initialized correctly: mint URL not specified yet. " @@ -189,7 +278,14 @@ class LedgerAPI: return self.keys async def _load_mint_keysets(self) -> List[str]: - # get all active keysets of this mint + """Loads the keyset IDs of the mint. + + Returns: + List[str]: list of keyset IDs of the mint + + Raises: + AssertionError: if no keysets are received from the mint + """ mint_keysets = [] try: mint_keysets = await self._get_keyset_ids(self.url) @@ -223,48 +319,8 @@ class LedgerAPI: if keyset_id: assert keyset_id in self.keysets, f"keyset {keyset_id} not active on mint" - @staticmethod - def _construct_outputs( - amounts: List[int], secrets: List[str] - ) -> Tuple[List[BlindedMessage], List[PrivateKey]]: - """Takes a list of amounts and secrets and returns outputs. - Outputs are blinded messages `outputs` and blinding factors `rs` - - Args: - amounts (List[int]): List of amounts - secrets (List[str]): List of secrets - - Returns: - Tuple[List[BlindedMessage], List[PrivateKey]]: Tuple of blinded messages and blinding factors - - Raises: - Exception: If len(amounts) != len(secrets) - """ - logger.trace(f"Constructing outputs.") - assert len(amounts) == len( - secrets - ), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}" - outputs: List[BlindedMessage] = [] - rs: List[PrivateKey] = [] - for secret, amount in zip(secrets, amounts): - B_, r = b_dhke.step1_alice(secret) - rs.append(r) - output: BlindedMessage = BlindedMessage( - amount=amount, B_=B_.serialize().hex() - ) - outputs.append(output) - logger.trace(f"Constructed {len(outputs)} outputs.") - return outputs, rs - - async def _check_used_secrets(self, secrets) -> None: - """Checks if any of the secrets have already been used - - Args: - secrets (List[str]): List of secrets to check - - Raises: - Exception: If any of the secrets have already been used - """ + async def _check_used_secrets(self, secrets): + """Checks if any of the secrets have already been used""" logger.trace("Checking secrets.") for s in secrets: if await secret_used(s, db=self.db): @@ -396,7 +452,7 @@ class LedgerAPI: return Invoice(amount=amount, pr=mint_response.pr, hash=mint_response.hash) @async_set_requests - async def mint(self, amounts, hash=None) -> List[Proof]: + async def mint(self, amounts: List[int], hash: Optional[str] = None) -> List[Proof]: """Mints new coins and returns a proof of promise. Args: @@ -409,9 +465,14 @@ class LedgerAPI: Raises: Exception: If the minting fails """ - secrets = [self._generate_secret() for s in range(len(amounts))] + # quirk: we skip bumping the secret counter in the database since we are + # not sure if the minting will succeed. If it succeeds, we will bump it + # in the next step. + secrets, rs, derivation_paths = await self.generate_n_secrets( + len(amounts), skip_bump=True + ) await self._check_used_secrets(secrets) - outputs, rs = self._construct_outputs(amounts, secrets) + outputs, rs = self._construct_outputs(amounts, secrets, rs) outputs_payload = PostMintRequest(outputs=outputs) logger.trace("Checking Lightning invoice. POST /mint") resp = self.s.post( @@ -432,7 +493,11 @@ class LedgerAPI: except: promises = PostMintResponse.parse_obj(reponse_dict).promises - return self._construct_proofs(promises, secrets, rs) + # bump secret counter in database + await bump_secret_derivation( + db=self.db, keyset_id=self.keyset_id, by=len(amounts) + ) + return self._construct_proofs(promises, secrets, rs, derivation_paths) @async_set_requests async def split( @@ -455,7 +520,7 @@ class LedgerAPI: split_payload = PostSplitRequest(proofs=proofs, amount=amount, outputs=outputs) # construct payload - def _splitrequest_include_fields(proofs): + def _splitrequest_include_fields(proofs: List[Proof]): """strips away fields from the model that aren't necessary for the /split""" proofs_include = {"id", "amount", "secret", "C", "p2shscript", "p2pksigs"} return { @@ -526,7 +591,7 @@ class LedgerAPI: payload = PostMeltRequest(proofs=proofs, pr=invoice, outputs=outputs) - def _meltrequest_include_fields(proofs): + def _meltrequest_include_fields(proofs: List[Proof]): """strips away fields from the model that aren't necessary for the /melt""" proofs_include = {"id", "amount", "secret", "C", "script"} return { @@ -544,29 +609,259 @@ class LedgerAPI: self.raise_on_error(return_dict) return GetMeltResponse.parse_obj(return_dict) + @async_set_requests + async def restore_promises( + self, outputs: List[BlindedMessage] + ) -> Tuple[List[BlindedMessage], List[BlindedSignature]]: + """ + Asks the mint to restore promises corresponding to outputs. + """ + payload = PostMintRequest(outputs=outputs) + resp = self.s.post(self.url + "/restore", json=payload.dict()) + resp.raise_for_status() + reponse_dict = resp.json() + self.raise_on_error(reponse_dict) + returnObj = PostRestoreResponse.parse_obj(reponse_dict) + return returnObj.outputs, returnObj.promises + class Wallet(LedgerAPI): """Minimal wallet wrapper.""" + mnemonic: str # holds mnemonic of the wallet + seed: bytes # holds private key of the wallet generated from the mnemonic + db: Database + bip32: BIP32 private_key: Optional[PrivateKey] = None - def __init__(self, url: str, db: str, name: str = "no_name"): - super().__init__(url) + def __init__( + self, + url: str, + db: str, + name: str = "no_name", + ): + """A Cashu wallet. + + Args: + url (str): URL of the mint. + db (str): Path to the database directory. + name (str, optional): Name of the wallet database file. Defaults to "no_name". + """ self.db = Database("wallet", db) self.proofs: List[Proof] = [] self.name = name + + super().__init__(url=url, db=self.db) logger.debug(f"Wallet initalized with mint URL {url}") - # temporarily, we use the NostrClient to generate keys + @classmethod + async def with_db( + cls, + url: str, + db: str, + name: str = "no_name", + skip_private_key: bool = False, + ): + """Initializes a wallet with a database and initializes the private key. + + Args: + url (str): URL of the mint. + db (str): Path to the database. + name (str, optional): Name of the wallet. Defaults to "no_name". + skip_private_key (bool, optional): If true, the private key is not initialized. Defaults to False. + + Returns: + Wallet: Initialized wallet. + """ + self = cls(url=url, db=db, name=name) + await self._migrate_database() + if not skip_private_key: + await self._init_private_key() + return self + + async def _migrate_database(self): try: - nostr_pk = NostrClient( - private_key=settings.nostr_private_key, connect=False - ).private_key - self.private_key = ( - PrivateKey(bytes.fromhex(nostr_pk.hex()), raw=True) or None - ) + await migrate_databases(self.db, migrations) except Exception as e: - pass + logger.error(f"Could not run migrations: {e}") + + async def _init_private_key(self, from_mnemonic: Optional[str] = None) -> None: + """Initializes the private key of the wallet from the mnemonic. + There are three ways to initialize the private key: + 1. If the database does not contain a seed, and no mnemonic is given, a new seed is generated. + 2. If the database does not contain a seed, and a mnemonic is given, the seed is generated from the mnemonic. + 3. If the database contains a seed, the seed is loaded from the database. + + If the mnemonic was not loaded from the database, the seed and mnemonic are stored in the database. + + Args: + from_mnemonic (Optional[str], optional): Mnemonic to use. Defaults to None. + + Raises: + ValueError: If the mnemonic is not BIP39 compliant. + """ + ret_db = await get_seed_and_mnemonic(self.db) + + mnemo = Mnemonic("english") + + if ret_db is None and from_mnemonic is None: + # if there is no seed in the database, generate a new one + mnemonic_str = mnemo.generate() + wallet_command_prefix_str = ( + f" --wallet {settings.wallet_name}" + if settings.wallet_name != "wallet" + else "" + ) + wallet_name = ( + f' for wallet "{settings.wallet_name}"' + if settings.wallet_name != "wallet" + else "" + ) + print( + f'Generated a new mnemonic{wallet_name}. To view it, run "cashu{wallet_command_prefix_str} info --mnemonic".' + ) + elif from_mnemonic: + # or use the one provided + mnemonic_str = from_mnemonic.lower().strip() + elif ret_db is not None: + # if there is a seed in the database, use it + _, mnemonic_str = ret_db[0], ret_db[1] + else: + logger.debug("No mnemonic provided") + return + + if not mnemo.check(mnemonic_str): + raise ValueError("Invalid mnemonic") + + self.seed = mnemo.to_seed(mnemonic_str) + self.mnemonic = mnemonic_str + + logger.debug(f"Using seed: {self.seed.hex()}") + logger.debug(f"Using mnemonic: {mnemonic_str}") + + # if no mnemonic was in the database, store the new one + if ret_db is None: + await store_seed_and_mnemonic( + self.db, seed=self.seed.hex(), mnemonic=mnemonic_str + ) + + try: + self.bip32 = BIP32.from_seed(self.seed) + self.private_key = PrivateKey( + self.bip32.get_privkey_from_path(f"m/129372'/0'/0'/0'") + ) + except ValueError: + raise ValueError("Invalid seed") + except Exception as e: + logger.error(e) + + async def _generate_secret(self, randombits=128) -> str: + """Returns base64 encoded deterministic random string. + + NOTE: This method should probably retire after `deterministic_secrets`. We are + deriving secrets from a counter but don't store the respective blinding factor. + We won't be able to restore any ecash generated with these secrets. + """ + secret_counter = await bump_secret_derivation( + db=self.db, keyset_id=self.keyset_id + ) + logger.trace(f"secret_counter: {secret_counter}") + s, _, _ = await self.generate_determinstic_secret(secret_counter) + # return s.decode("utf-8") + return hashlib.sha256(s).hexdigest() + + async def generate_determinstic_secret( + self, counter: int + ) -> Tuple[bytes, bytes, str]: + """ + Determinstically generates two secrets (one as the secret message, + one as the blinding factor). + """ + assert self.mint_info.pubkey, "Mint info not loaded yet." + assert self.bip32, "BIP32 not initialized yet." + # integer keyset id modulo max number of bip32 child keys + keyest_id = int.from_bytes(base64.b64decode(self.keyset_id), "big") % ( + 2**31 - 1 + ) + logger.trace(f"keyset id: {self.keyset_id} becomes {keyest_id}") + token_derivation_path = f"m/129372'/0'/{keyest_id}'/{counter}'" + # for secret + secret_derivation_path = f"{token_derivation_path}/0" + logger.trace(f"secret derivation path: {secret_derivation_path}") + secret = self.bip32.get_privkey_from_path(secret_derivation_path) + # blinding factor + r_derivation_path = f"{token_derivation_path}/1" + logger.trace(f"r derivation path: {r_derivation_path}") + r = self.bip32.get_privkey_from_path(r_derivation_path) + return secret, r, token_derivation_path + + async def generate_n_secrets( + self, n: int = 1, skip_bump: bool = False + ) -> Tuple[List[str], List[PrivateKey], List[str]]: + """Generates n secrets and blinding factors and returns a tuple of secrets, + blinding factors, and derivation paths. + + Args: + n (int, optional): Number of secrets to generate. Defaults to 1. + skip_bump (bool, optional): Skip increment of secret counter in the database. + You want to set this to false if you don't know whether the following operation + will succeed or not (like a POST /mint request). Defaults to False. + + Returns: + Tuple[List[str], List[PrivateKey], List[str]]: Secrets, blinding factors, derivation paths + + """ + secret_counters_start = await bump_secret_derivation( + db=self.db, keyset_id=self.keyset_id, by=n, skip=skip_bump + ) + logger.trace(f"secret_counters_start: {secret_counters_start}") + secret_counters = list(range(secret_counters_start, secret_counters_start + n)) + logger.trace( + f"Generating secret nr {secret_counters[0]} to {secret_counters[-1]}." + ) + secrets_rs_derivationpaths = [ + await self.generate_determinstic_secret(s) for s in secret_counters + ] + # secrets are supplied as str + secrets = [hashlib.sha256(s[0]).hexdigest() for s in secrets_rs_derivationpaths] + # rs are supplied as PrivateKey + rs = [PrivateKey(privkey=s[1], raw=True) for s in secrets_rs_derivationpaths] + + derivation_paths = [s[2] for s in secrets_rs_derivationpaths] + # sanity check to make sure we're not reusing secrets + # NOTE: this step is probably wasting more resources than it helps + await self._check_used_secrets(secrets) + + return secrets, rs, derivation_paths + + async def generate_secrets_from_to( + self, from_counter: int, to_counter: int + ) -> Tuple[List[str], List[PrivateKey], List[str]]: + """Generates secrets and blinding factors from `from_counter` to `to_counter` + + Args: + from_counter (int): Start counter + to_counter (int): End counter + + Returns: + Tuple[List[str], List[PrivateKey], List[str]]: Secrets, blinding factors, derivation paths + + Raises: + ValueError: If `from_counter` is larger than `to_counter` + """ + assert ( + from_counter <= to_counter + ), "from_counter must be smaller than to_counter" + secret_counters = [c for c in range(from_counter, to_counter + 1)] + secrets_rs_derivationpaths = [ + await self.generate_determinstic_secret(s) for s in secret_counters + ] + # secrets are supplied as str + secrets = [hashlib.sha256(s[0]).hexdigest() for s in secrets_rs_derivationpaths] + # rs are supplied as PrivateKey + rs = [PrivateKey(privkey=s[1], raw=True) for s in secrets_rs_derivationpaths] + derivation_paths = [s[2] for s in secrets_rs_derivationpaths] + return secrets, rs, derivation_paths # ---------- API ---------- @@ -580,7 +875,7 @@ class Wallet(LedgerAPI): """ await super()._load_mint(keyset_id) - async def load_proofs(self, reload: bool = False): + async def load_proofs(self, reload: bool = False) -> None: """Load all proofs from the database.""" if self.proofs and not reload: @@ -588,7 +883,15 @@ class Wallet(LedgerAPI): return self.proofs = await get_proofs(db=self.db) - async def request_mint(self, amount): + async def request_mint(self, amount: int) -> Invoice: + """Request a Lightning invoice for minting tokens. + + Args: + amount (int): Amount for Lightning invoice in satoshis + + Returns: + Invoice: Lightning invoice + """ invoice = await super().request_mint(amount) invoice.time_created = int(time.time()) await store_lightning_invoice(db=self.db, invoice=invoice) @@ -599,7 +902,7 @@ class Wallet(LedgerAPI): amount: int, split: Optional[List[int]] = None, hash: Optional[str] = None, - ): + ) -> List[Proof]: """Mint tokens of a specific amount after an invoice has been paid. Args: @@ -703,10 +1006,22 @@ class Wallet(LedgerAPI): return proofs async def add_witnesses_to_proofs(self, proofs: List[Proof]) -> List[Proof]: - """Adds witnesses to proofs for P2SH or P2PK redemption.""" + """Adds witnesses to proofs for P2SH or P2PK redemption. - p2sh_script, p2sh_signature = None, None - p2pk_signatures = None + This method parses the secret of each proof and determines the correct + witness type and adds it to the proof if we have it available. + + Note: In order for this method to work, all proofs must have the same secret type. + This is because we use a single P2SH script and signature pair for all tokens in proofs. + + For P2PK, we use an individual signature for each token in proofs. + + Args: + proofs (List[Proof]): List of proofs to add witnesses to + + Returns: + List[Proof]: List of proofs with witnesses added + """ # iterate through proofs and produce witnesses for each @@ -735,7 +1050,14 @@ class Wallet(LedgerAPI): async def redeem( self, proofs: List[Proof], - ): + ) -> Tuple[List[Proof], List[Proof]]: + """Redeem proofs by sending them to yourself (by calling a split).) + Calls `add_witnesses_to_proofs` which parses all proofs and checks whether their + secrets corresponds to any locks that we have the unlock conditions for. If so, + it adds the unlock conditions to the proofs. + Args: + proofs (List[Proof]): Proofs to be redeemed. + """ return await self.split(proofs, sum_proofs(proofs)) async def split( @@ -743,7 +1065,21 @@ class Wallet(LedgerAPI): proofs: List[Proof], amount: int, secret_lock: Optional[Secret] = None, - ): + ) -> Tuple[List[Proof], List[Proof]]: + """Split proofs into two sets of proofs at a given amount. + + Args: + proofs (List[Proof]): Input proofs to split. + amount (int): Amount to split at. + secret_lock (Optional[Secret], optional): Secret to lock outputs to. Defaults to None. + + Raises: + Exception: Raises exception if no proofs have been provided + Exception: Raises exception if no proofs are returned after splitting + + Returns: + Tuple[List[Proof], List[Proof]]: Two sets of proofs after splitting. + """ assert len(proofs) > 0, ValueError("no proofs provided.") # potentially add witnesses to unlock provided proofs (if they indicate one) @@ -758,19 +1094,24 @@ class Wallet(LedgerAPI): amounts = frst_outputs + scnd_outputs # generate secrets for new outputs if secret_lock is None: - # generate all secrets randomly - secrets = [self._generate_secret() for _ in range(len(amounts))] + secrets, rs, derivation_paths = await self.generate_n_secrets(len(amounts)) else: - # use provided secret_lock to generate secrets + # NOTE: we use random blinding factors for P2SH, we won't be able to + # restore these tokens from a backup + rs = [] + # generate secrets for receiver secret_locks = [secret_lock.serialize() for i in range(len(scnd_outputs))] logger.debug(f"Creating proofs with custom secrets: {secret_locks}") assert len(secret_locks) == len( scnd_outputs ), "number of secret_locks does not match number of ouptus." - # append custom locks (to send) to randomly generated secrets (to keep) + # append predefined secrets (to send) to random secrets (to keep) + # generate sercets to keep secrets = [ - self._generate_secret() for s in range(len(frst_outputs)) + await self._generate_secret() for s in range(len(frst_outputs)) ] + secret_locks + # TODO: derive derivation paths from secrets + derivation_paths = ["custom"] * len(secrets) assert len(secrets) == len( amounts @@ -779,7 +1120,7 @@ class Wallet(LedgerAPI): await self._check_used_secrets(secrets) # construct outputs - outputs, rs = self._construct_outputs(amounts, secrets) + outputs, rs = self._construct_outputs(amounts, secrets, rs) # potentially add witnesses to outputs based on what requirement the proofs indicate outputs = await self.add_witnesses_to_outputs(proofs, outputs) @@ -791,10 +1132,16 @@ class Wallet(LedgerAPI): # Construct proofs from returned promises (i.e., unblind the signatures) frst_proofs = self._construct_proofs( - promises_fst, secrets[: len(promises_fst)], rs[: len(promises_fst)] + promises_fst, + secrets[: len(promises_fst)], + rs[: len(promises_fst)], + derivation_paths, ) scnd_proofs = self._construct_proofs( - promises_snd, secrets[len(promises_fst) :], rs[len(promises_fst) :] + promises_snd, + secrets[len(promises_fst) :], + rs[len(promises_fst) :], + derivation_paths, ) # remove used proofs from wallet and add new ones @@ -804,20 +1151,30 @@ class Wallet(LedgerAPI): self.proofs += frst_proofs + scnd_proofs # store new proofs in database await self._store_proofs(frst_proofs + scnd_proofs) - # invalidate used proofs in database - for proof in proofs: - await invalidate_proof(proof, db=self.db) + # invalidate used proofs + async with self.db.connect() as conn: + for proof in proofs: + await invalidate_proof(proof, db=self.db, conn=conn) return frst_proofs, scnd_proofs - async def pay_lightning(self, proofs: List[Proof], invoice: str, fee_reserve: int): - """Pays a lightning invoice""" + async def pay_lightning( + self, proofs: List[Proof], invoice: str, fee_reserve_sat: int + ) -> bool: + """Pays a lightning invoice and returns the status of the payment. + + Args: + proofs (List[Proof]): List of proofs to be spent. + invoice (str): Lightning invoice to be paid. + fee_reserve_sat (int): Amount of fees to be reserved for the payment. + + """ # Generate a number of blank outputs for any overpaid fees. As described in # NUT-08, the mint will imprint these outputs with a value depending on the # amount of fees we overpaid. - n_return_outputs = calculate_number_of_blank_outputs(fee_reserve) - secrets = [self._generate_secret() for _ in range(n_return_outputs)] - outputs, rs = self._construct_outputs(n_return_outputs * [1], secrets) + n_return_outputs = calculate_number_of_blank_outputs(fee_reserve_sat) + secrets, rs, derivation_paths = await self.generate_n_secrets(n_return_outputs) + outputs, rs = self._construct_outputs(n_return_outputs * [1], secrets, rs) status = await super().pay_lightning(proofs, invoice, outputs) @@ -833,7 +1190,7 @@ class Wallet(LedgerAPI): hash="", ) # we have a unique constraint on the hash, so we generate a random one if it doesn't exist - invoice_obj.hash = invoice_obj.hash or self._generate_secret() + invoice_obj.hash = invoice_obj.hash or await self._generate_secret() await store_lightning_invoice(db=self.db, invoice=invoice_obj) # handle change and produce proofs @@ -842,6 +1199,7 @@ class Wallet(LedgerAPI): status.change, secrets[: len(status.change)], rs[: len(status.change)], + derivation_paths[: len(status.change)], ) logger.debug(f"Received change: {sum_proofs(change_proofs)} sat") await self._store_proofs(change_proofs) @@ -856,28 +1214,32 @@ class Wallet(LedgerAPI): # ---------- TOKEN MECHANIS ---------- async def _store_proofs(self, proofs): - for proof in proofs: - await store_proof(proof, db=self.db) + async with self.db.connect() as conn: + for proof in proofs: + await store_proof(proof, db=self.db, conn=conn) @staticmethod def _get_proofs_per_keyset(proofs: List[Proof]): return {key: list(group) for key, group in groupby(proofs, lambda p: p.id)} # type: ignore - async def _get_proofs_per_minturl(self, proofs: List[Proof]): - ret = {} + async def _get_proofs_per_minturl( + self, proofs: List[Proof] + ) -> Dict[str, List[Proof]]: + ret: Dict[str, List[Proof]] = {} for id in set([p.id for p in proofs]): if id is None: continue keyset_crud = await get_keyset(id=id, db=self.db) assert keyset_crud is not None, "keyset not found" keyset: WalletKeyset = keyset_crud + assert keyset.mint_url if keyset.mint_url not in ret: ret[keyset.mint_url] = [p for p in proofs if p.id == id] else: ret[keyset.mint_url].extend([p for p in proofs if p.id == id]) return ret - def _get_proofs_keysets(self, proofs: List[Proof]): + def _get_proofs_keysets(self, proofs: List[Proof]) -> List[str]: """Extracts all keyset ids from a list of proofs. Args: @@ -886,7 +1248,7 @@ class Wallet(LedgerAPI): keysets: List[str] = [proof.id for proof in proofs if proof.id] return keysets - async def _get_keyset_urls(self, keysets: List[str]): + async def _get_keyset_urls(self, keysets: List[str]) -> Dict[str, List[str]]: """Retrieves the mint URLs for a list of keyset id's from the wallet's database. Returns a dictionary from URL to keyset ID @@ -904,10 +1266,17 @@ class Wallet(LedgerAPI): ) return mint_urls - async def _make_token(self, proofs: List[Proof], include_mints=True): + async def _make_token(self, proofs: List[Proof], include_mints=True) -> TokenV3: """ Takes list of proofs and produces a TokenV3 by looking up the mint URLs by the keyset id from the database. + + Args: + proofs (List[Proof]): List of proofs to be included in the token + include_mints (bool, optional): Whether to include the mint URLs in the token. Defaults to True. + + Returns: + TokenV3: TokenV3 object """ token = TokenV3() @@ -931,15 +1300,22 @@ class Wallet(LedgerAPI): async def serialize_proofs( self, proofs: List[Proof], include_mints=True, legacy=False - ): - """ - Produces sharable token with proofs and mint information. + ) -> str: + """Produces sharable token with proofs and mint information. + + Args: + proofs (List[Proof]): List of proofs to be included in the token + include_mints (bool, optional): Whether to include the mint URLs in the token. Defaults to True. + legacy (bool, optional): Whether to produce a legacy V2 token. Defaults to False. + + Returns: + str: Serialized Cashu token """ if legacy: # V2 tokens - token = await self._make_token_v2(proofs, include_mints) - return await self._serialize_token_base64_tokenv2(token) + token_v2 = await self._make_token_v2(proofs, include_mints) + return await self._serialize_token_base64_tokenv2(token_v2) # # deprecated code for V1 tokens # proofs_serialized = [p.to_dict() for p in proofs] @@ -951,7 +1327,7 @@ class Wallet(LedgerAPI): token = await self._make_token(proofs, include_mints) return token.serialize() - async def _make_token_v2(self, proofs: List[Proof], include_mints=True): + async def _make_token_v2(self, proofs: List[Proof], include_mints=True) -> TokenV2: """ Takes list of proofs and produces a TokenV2 by looking up the keyset id and mint URLs from the database. @@ -983,7 +1359,7 @@ class Wallet(LedgerAPI): token.mints = list(mints.values()) return token - async def _serialize_token_base64_tokenv2(self, token: TokenV2): + async def _serialize_token_base64_tokenv2(self, token: TokenV2) -> str: """ Takes a TokenV2 and serializes it in urlsafe_base64. """ @@ -993,7 +1369,9 @@ class Wallet(LedgerAPI): ).decode() return token_base64 - async def _select_proofs_to_send(self, proofs: List[Proof], amount_to_send: int): + async def _select_proofs_to_send( + self, proofs: List[Proof], amount_to_send: int + ) -> List[Proof]: """ Selects proofs that can be used with the current mint. Implements a simple coin selection algorithm. @@ -1032,10 +1410,17 @@ class Wallet(LedgerAPI): while sum_proofs(send_proofs) < amount_to_send: proof_to_add = sorted_proofs_of_current_keyset.pop() send_proofs.append(proof_to_add) + + logger.debug(f"selected proof amounts: {[p.amount for p in send_proofs]}") return send_proofs - async def set_reserved(self, proofs: List[Proof], reserved: bool): - """Mark a proof as reserved to avoid reuse or delete marking.""" + async def set_reserved(self, proofs: List[Proof], reserved: bool) -> None: + """Mark a proof as reserved or reset it in the wallet db to avoid reuse when it is sent. + + Args: + proofs (List[Proof]): List of proofs to mark as reserved + reserved (bool): Whether to mark the proofs as reserved or not + """ uuid_str = str(uuid.uuid1()) for proof in proofs: proof.reserved = True @@ -1043,12 +1428,17 @@ class Wallet(LedgerAPI): proof, reserved=reserved, send_id=uuid_str, db=self.db ) - async def invalidate(self, proofs: List[Proof], check_spendable=True): + async def invalidate( + self, proofs: List[Proof], check_spendable=True + ) -> List[Proof]: """Invalidates all unspendable tokens supplied in proofs. Args: 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. + + Returns: + List[Proof]: List of proofs that are still spendable. """ invalidated_proofs: List[Proof] = [] if check_spendable: @@ -1059,12 +1449,20 @@ class Wallet(LedgerAPI): else: invalidated_proofs = proofs - for p in invalidated_proofs: - await invalidate_proof(p, db=self.db) + if invalidated_proofs: + logger.debug( + f"Invalidating {len(invalidated_proofs)} proofs worth {sum_proofs(invalidated_proofs)} sat." + ) + + async with self.db.connect() as conn: + for p in invalidated_proofs: + await invalidate_proof(p, db=self.db, conn=conn) + invalidate_secrets = [p.secret for p in invalidated_proofs] self.proofs = list( filter(lambda p: p.secret not in invalidate_secrets, self.proofs) ) + return [p for p in proofs if p not in invalidated_proofs] # ---------- TRANSACTION HELPERS ---------- @@ -1254,3 +1652,100 @@ class Wallet(LedgerAPI): for key, proofs in balances.items() } return dict(sorted(balances_return.items(), key=lambda item: item[0])) # type: ignore + + async def restore_wallet_from_mnemonic( + self, mnemonic: Optional[str], to: int = 2, batch: int = 25 + ) -> None: + """Restores the wallet from a mnemonic + + Args: + mnemonic (Optional[str]): The mnemonic to restore the wallet from. If None, the mnemonic is loaded from the db. + to (int, optional): The number of consecutive empty reponses to stop restoring. Defaults to 2. + batch (int, optional): The number of proofs to restore in one batch. Defaults to 25. + """ + await self._init_private_key(mnemonic) + await self.load_mint() + print("Restoring tokens...") + stop_counter = 0 + # we get the current secret counter and restore from there on + spendable_proofs = [] + counter_before = await bump_secret_derivation( + db=self.db, keyset_id=self.keyset_id, by=0 + ) + if counter_before != 0: + print("This wallet has already been used. Restoring from it's last state.") + i = counter_before + n_last_restored_proofs = 0 + while stop_counter < to: + print(f"Restoring token {i} to {i + batch}...") + restored_proofs = await self.restore_promises(i, i + batch - 1) + if len(restored_proofs) == 0: + stop_counter += 1 + spendable_proofs = await self.invalidate(restored_proofs) + if len(spendable_proofs): + n_last_restored_proofs = len(spendable_proofs) + print(f"Restored {sum_proofs(restored_proofs)} sat") + i += batch + + # restore the secret counter to its previous value for the last round + revert_counter_by = batch * to + n_last_restored_proofs + logger.debug(f"Reverting secret counter by {revert_counter_by}") + before = await bump_secret_derivation( + db=self.db, + keyset_id=self.keyset_id, + by=-revert_counter_by, + ) + logger.debug( + f"Secret counter reverted from {before} to {before - revert_counter_by}" + ) + if n_last_restored_proofs == 0: + print("No tokens restored.") + return + + async def restore_promises(self, from_counter: int, to_counter: int) -> List[Proof]: + """Restores promises from a given range of counters. This is for restoring a wallet from a mnemonic. + + Args: + from_counter (int): Counter for the secret derivation to start from + to_counter (int): Counter for the secret derivation to end at + + Returns: + List[Proof]: List of restored proofs + """ + # we regenerate the secrets and rs for the given range + secrets, rs, derivation_paths = await self.generate_secrets_from_to( + from_counter, to_counter + ) + # we don't know the amount but luckily the mint will tell us so we use a dummy amount here + amounts_dummy = [1] * len(secrets) + # we generate outptus from deterministic secrets and rs + regenerated_outputs, _ = self._construct_outputs(amounts_dummy, secrets, rs) + # we ask the mint to reissue the promises + # restored_outputs is there so we can match the promises to the secrets and rs + restored_outputs, restored_promises = await super().restore_promises( + regenerated_outputs + ) + # now we need to filter out the secrets and rs that had a match + matching_indices = [ + idx + for idx, val in enumerate(regenerated_outputs) + if val.B_ in [o.B_ for o in restored_outputs] + ] + secrets = [secrets[i] for i in matching_indices] + rs = [rs[i] for i in matching_indices] + # now we can construct the proofs with the secrets and rs + proofs = self._construct_proofs( + restored_promises, secrets, rs, derivation_paths + ) + logger.debug(f"Restored {len(restored_promises)} promises") + await self._store_proofs(proofs) + + # append proofs to proofs in memory so the balance updates + for proof in proofs: + if proof.secret not in [p.secret for p in self.proofs]: + self.proofs.append(proof) + + await set_secret_derivation( + db=self.db, keyset_id=self.keyset_id, counter=to_counter + 1 + ) + return proofs diff --git a/poetry.lock b/poetry.lock index 841eed4..202faac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,44 +2,75 @@ [[package]] name = "anyio" -version = "3.6.2" +version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" files = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] [package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] [[package]] name = "attrs" -version = "22.2.0" +version = "23.1.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "base58" +version = "2.1.1" +description = "Base58 and Base58Check implementation." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, + {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, ] [package.extras] -cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] -tests = ["attrs[tests-no-zope]", "zope.interface"] -tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] +tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"] [[package]] name = "bech32" @@ -53,6 +84,22 @@ files = [ {file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"}, ] +[[package]] +name = "bip32" +version = "3.4" +description = "Minimalistic implementation of the BIP32 key derivation scheme" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "bip32-3.4-py3-none-any.whl", hash = "sha256:c18099c25cabc087d081142d040eacb6fdf09f2c61fde3247e1f61313d1c0d26"}, + {file = "bip32-3.4.tar.gz", hash = "sha256:09213225d99ede936c39a4f5a520549d8ecc49ebe3fa15608a17ea4aa9f61a10"}, +] + +[package.dependencies] +base58 = ">=2.0,<3.0" +coincurve = ">=15.0,<19" + [[package]] name = "bitstring" version = "3.1.9" @@ -105,14 +152,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, ] [[package]] @@ -194,118 +241,161 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.0.1" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, - {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] [[package]] name = "click" -version = "8.1.3" +version = "8.1.5" description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.5-py3-none-any.whl", hash = "sha256:e576aa487d679441d7d30abb87e1b43d24fc53bffb8758443b1a9e1cee504548"}, + {file = "click-8.1.5.tar.gz", hash = "sha256:4be4b1af8d665c6d942909916d31a213a106800c47d0eeba73d34da3cbc11367"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +[[package]] +name = "coincurve" +version = "18.0.0" +description = "Cross-platform Python CFFI bindings for libsecp256k1" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coincurve-18.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b1a42eba91b9e4f833309e94bc6a270b1700cb4567d4809ef91f00968b57925"}, + {file = "coincurve-18.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:116bf1b60a6e72e23c6b153d7c79f0e565d82973d917a3cecf655ffb29263163"}, + {file = "coincurve-18.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d53e2a268142924c24e9b786b3e6c3603fae54bb8211560036b0e9ce6a9f2dbc"}, + {file = "coincurve-18.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b31ab366fadff16ecfdde96ffc07e70fee83850f88bd1f985a8b4977a68bbfb"}, + {file = "coincurve-18.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e3c37cfadac6896668a130ea46296a3dfdeea0160fd66a51e377ad00795269"}, + {file = "coincurve-18.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f3e5f2a2d774050b3ea8bf2167f2d598fde58d7690779931516714d98b65d884"}, + {file = "coincurve-18.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83379dd70291480df2052554851bfd17444c003aef7c4bb02d96d73eec69fe28"}, + {file = "coincurve-18.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:33678f6b43edbeab6605584c725305f4f814239780c53eba0f8e4bc4a52b1d1a"}, + {file = "coincurve-18.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f40646d5f29ac9026f8cc1b368bc9ab68710fad055b64fbec020f9bbfc99b242"}, + {file = "coincurve-18.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:779da694dea1b1d09e16b00e079f6a1195290ce9568f39c95cddf35f1f49ec49"}, + {file = "coincurve-18.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7844f01904e32317a00696a27fd771860e53a2fa62e5c66eace9337d2742c9e6"}, + {file = "coincurve-18.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257c6171cd0301c119ef41360f0d0c2fb5cc288717b33d3bd5482a4c9ae04551"}, + {file = "coincurve-18.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8bcb9c40fd730cf377fa448f1304355d6497fb3d00b7b0a69a10dfcc14a6d28"}, + {file = "coincurve-18.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e3abb7f65e2b5fb66a15e374faeaafe6700fdb83fb66d1873ddff91c395a3b74"}, + {file = "coincurve-18.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f44b9ba588b34795d1b4074f9a9fa372adef3fde58300bf32f40a69e8cd72a23"}, + {file = "coincurve-18.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:908467330cd3047c71105a08394c4f3e7dce76e4371b030ba8b0ef863013e3ca"}, + {file = "coincurve-18.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:599b1b3cf097cae920d97f31a5b8e8aff185ca8fa5d8a785b2edf7b199fb9731"}, + {file = "coincurve-18.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d2c20d108580bce5efedb980688031462168f4de2446de95898b48a249127a2"}, + {file = "coincurve-18.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eba563f7f70c10323227d1890072172bd84df6f814c9a6b012033b214426b6cf"}, + {file = "coincurve-18.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:412a06b7d1b8229f25318f05e76310298da5ad55d73851eabac7ddfdcdc5bff4"}, + {file = "coincurve-18.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:286969b6f789bbd9d744d28350a3630c1cb3ee045263469a28892f70a4a6654a"}, + {file = "coincurve-18.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:14700463009c7d799a746929728223aa53ff1ece394ea408516d98d637434883"}, + {file = "coincurve-18.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7f1142252e870f091b2c2c21cc1fadfdd29af23d02e99f29add0f14d1ba94b4c"}, + {file = "coincurve-18.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cd11d2ca5b7e989c5ce1af217a2ad78c19c21afca786f198d1b1a408d6f408dc"}, + {file = "coincurve-18.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1bce17d7475cee9db2c2fa7af07eaab582732b378acf6dcaee417de1df2d8661"}, + {file = "coincurve-18.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab662b67454fea7f0a5ae855ba6ad9410bcaebe68b97f4dade7b5944dec3a11"}, + {file = "coincurve-18.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b9ced9cce32dabb4bc15fa6449252fa51efddf0268481973e4c3772a5a68c6"}, + {file = "coincurve-18.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d05641cf31d68514c47cb54105d20acbae79fc3ee3942454eaaf411babb3f880"}, + {file = "coincurve-18.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a7b31efe56b3f6434828ad5f6ecde4a95747bb69b59032746482eebb8f3456a4"}, + {file = "coincurve-18.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d95103ed43df855121cd925869ae2589360a8d94fcd61b236958deacfb9a359"}, + {file = "coincurve-18.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:abeb4c1d78e1a81a3f1c99a406cd858669582ada2d976e876ef694f57dec95ca"}, + {file = "coincurve-18.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fceca9d6ecaa1e8f891675e4f4ff530d54e41c648fc6e8a816835ffa640fa899"}, + {file = "coincurve-18.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e009f06287507158f16c82cc313c0f3bfd0e9ec1e82d1a4d5fa1c5b6c0060f69"}, + {file = "coincurve-18.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a0c0c1e492ef08efe99d25a23d535e2bff667bbef43d71a6f8893ae811b3d81"}, + {file = "coincurve-18.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3caf58877bcf41eb4c1be7a2d54317f0b31541d99ba248dae28821b19c52a0db"}, + {file = "coincurve-18.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8964e680c622a2b5eea940abdf51c77c1bd3d4fde2a04cec2420bf91981b198a"}, + {file = "coincurve-18.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73e464e0ace77c686fdc54590e5592905b6802f9fc20a0c023f0b1585669d6a3"}, + {file = "coincurve-18.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ba9eaddd50a2ce0d891af7cee11c2e048d1f0f44bf87db00a5c4b1eee7e3391b"}, + {file = "coincurve-18.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8290903d4629f27f9f3cdeec72ffa97536c5a6ed5ba7e3413b2707991c650fbe"}, + {file = "coincurve-18.0.0-py3-none-win32.whl", hash = "sha256:c60690bd7704d8563968d2dded33eb514875a52b5964f085409965ad041b2555"}, + {file = "coincurve-18.0.0-py3-none-win_amd64.whl", hash = "sha256:704d1abf2e78def33988368592233a8ec9b98bfc45dfa2ec9e898adfad46e5ad"}, + {file = "coincurve-18.0.0.tar.gz", hash = "sha256:c86626afe417a09d8e80e56780efcae3ae516203b23b5ade84813916e1c94fc1"}, +] + +[package.dependencies] +asn1crypto = "*" +cffi = ">=1.3.0" + [[package]] name = "colorama" version = "0.4.6" @@ -320,63 +410,72 @@ files = [ [[package]] name = "coverage" -version = "7.2.1" +version = "7.2.7" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-7.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49567ec91fc5e0b15356da07a2feabb421d62f52a9fff4b1ec40e9e19772f5f8"}, - {file = "coverage-7.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03"}, - {file = "coverage-7.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3004765bca3acd9e015794e5c2f0c9a05587f5e698127ff95e9cfba0d3f29339"}, - {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cca7c0b7f5881dfe0291ef09ba7bb1582cb92ab0aeffd8afb00c700bf692415a"}, - {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2167d116309f564af56f9aa5e75ef710ef871c5f9b313a83050035097b56820"}, - {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cb5f152fb14857cbe7f3e8c9a5d98979c4c66319a33cad6e617f0067c9accdc4"}, - {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:87dc37f16fb5e3a28429e094145bf7c1753e32bb50f662722e378c5851f7fdc6"}, - {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e191a63a05851f8bce77bc875e75457f9b01d42843f8bd7feed2fc26bbe60833"}, - {file = "coverage-7.2.1-cp310-cp310-win32.whl", hash = "sha256:e3ea04b23b114572b98a88c85379e9e9ae031272ba1fb9b532aa934c621626d4"}, - {file = "coverage-7.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:0cf557827be7eca1c38a2480484d706693e7bb1929e129785fe59ec155a59de6"}, - {file = "coverage-7.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:570c21a29493b350f591a4b04c158ce1601e8d18bdcd21db136fbb135d75efa6"}, - {file = "coverage-7.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e872b082b32065ac2834149dc0adc2a2e6d8203080501e1e3c3c77851b466f9"}, - {file = "coverage-7.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac6343bae03b176e9b58104a9810df3cdccd5cfed19f99adfa807ffbf43cf9b"}, - {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abacd0a738e71b20e224861bc87e819ef46fedba2fb01bc1af83dfd122e9c319"}, - {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9256d4c60c4bbfec92721b51579c50f9e5062c21c12bec56b55292464873508"}, - {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80559eaf6c15ce3da10edb7977a1548b393db36cbc6cf417633eca05d84dd1ed"}, - {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bd7e628f6c3ec4e7d2d24ec0e50aae4e5ae95ea644e849d92ae4805650b4c4e"}, - {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09643fb0df8e29f7417adc3f40aaf379d071ee8f0350ab290517c7004f05360b"}, - {file = "coverage-7.2.1-cp311-cp311-win32.whl", hash = "sha256:1b7fb13850ecb29b62a447ac3516c777b0e7a09ecb0f4bb6718a8654c87dfc80"}, - {file = "coverage-7.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:617a94ada56bbfe547aa8d1b1a2b8299e2ec1ba14aac1d4b26a9f7d6158e1273"}, - {file = "coverage-7.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8649371570551d2fd7dee22cfbf0b61f1747cdfb2b7587bb551e4beaaa44cb97"}, - {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2b9b5e70a21474c105a133ba227c61bc95f2ac3b66861143ce39a5ea4b3f84"}, - {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82c988954722fa07ec5045c57b6d55bc1a0890defb57cf4a712ced65b26ddd"}, - {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:861cc85dfbf55a7a768443d90a07e0ac5207704a9f97a8eb753292a7fcbdfcfc"}, - {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0339dc3237c0d31c3b574f19c57985fcbe494280153bbcad33f2cdf469f4ac3e"}, - {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5928b85416a388dd557ddc006425b0c37e8468bd1c3dc118c1a3de42f59e2a54"}, - {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d3843ca645f62c426c3d272902b9de90558e9886f15ddf5efe757b12dd376f5"}, - {file = "coverage-7.2.1-cp37-cp37m-win32.whl", hash = "sha256:6a034480e9ebd4e83d1aa0453fd78986414b5d237aea89a8fdc35d330aa13bae"}, - {file = "coverage-7.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6fce673f79a0e017a4dc35e18dc7bb90bf6d307c67a11ad5e61ca8d42b87cbff"}, - {file = "coverage-7.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f099da6958ddfa2ed84bddea7515cb248583292e16bb9231d151cd528eab657"}, - {file = "coverage-7.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97a3189e019d27e914ecf5c5247ea9f13261d22c3bb0cfcfd2a9b179bb36f8b1"}, - {file = "coverage-7.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a81dbcf6c6c877986083d00b834ac1e84b375220207a059ad45d12f6e518a4e3"}, - {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d2c3dde4c0b9be4b02067185136b7ee4681978228ad5ec1278fa74f5ca3e99"}, - {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a209d512d157379cc9ab697cbdbb4cfd18daa3e7eebaa84c3d20b6af0037384"}, - {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f3d07edb912a978915576a776756069dede66d012baa503022d3a0adba1b6afa"}, - {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8dca3c1706670297851bca1acff9618455122246bdae623be31eca744ade05ec"}, - {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b1991a6d64231a3e5bbe3099fb0dd7c9aeaa4275ad0e0aeff4cb9ef885c62ba2"}, - {file = "coverage-7.2.1-cp38-cp38-win32.whl", hash = "sha256:22c308bc508372576ffa3d2dbc4824bb70d28eeb4fcd79d4d1aed663a06630d0"}, - {file = "coverage-7.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0c0d46de5dd97f6c2d1b560bf0fcf0215658097b604f1840365296302a9d1fb"}, - {file = "coverage-7.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4dd34a935de268a133e4741827ae951283a28c0125ddcdbcbba41c4b98f2dfef"}, - {file = "coverage-7.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f8318ed0f3c376cfad8d3520f496946977abde080439d6689d7799791457454"}, - {file = "coverage-7.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:834c2172edff5a08d78e2f53cf5e7164aacabeb66b369f76e7bb367ca4e2d993"}, - {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4d70c853f0546855f027890b77854508bdb4d6a81242a9d804482e667fff6e6"}, - {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a6450da4c7afc4534305b2b7d8650131e130610cea448ff240b6ab73d7eab63"}, - {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99f4dd81b2bb8fc67c3da68b1f5ee1650aca06faa585cbc6818dbf67893c6d58"}, - {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bdd3f2f285ddcf2e75174248b2406189261a79e7fedee2ceeadc76219b6faa0e"}, - {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f29351393eb05e6326f044a7b45ed8e38cb4dcc38570d12791f271399dc41431"}, - {file = "coverage-7.2.1-cp39-cp39-win32.whl", hash = "sha256:e2b50ebc2b6121edf352336d503357321b9d8738bb7a72d06fc56153fd3f4cd8"}, - {file = "coverage-7.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a"}, - {file = "coverage-7.2.1-pp37.pp38.pp39-none-any.whl", hash = "sha256:436313d129db7cf5b4ac355dd2bd3f7c7e5294af077b090b85de75f8458b8616"}, - {file = "coverage-7.2.1.tar.gz", hash = "sha256:c77f2a9093ccf329dd523a9b2b3c854c20d2a3d968b6def3b820272ca6732242"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, ] [package.dependencies] @@ -469,14 +568,14 @@ tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] [[package]] name = "exceptiongroup" -version = "1.1.0" +version = "1.1.2" description = "Backport of PEP 654 (exception groups)" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, - {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, ] [package.extras] @@ -665,6 +764,18 @@ docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sp lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] +[[package]] +name = "mnemonic" +version = "0.20" +description = "Implementation of Bitcoin BIP-0039" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mnemonic-0.20-py3-none-any.whl", hash = "sha256:acd2168872d0379e7a10873bb3e12bf6c91b35de758135c4fbd1015ef18fafc5"}, + {file = "mnemonic-0.20.tar.gz", hash = "sha256:7c6fb5639d779388027a77944680aee4870f0fcd09b1e42a5525ee2ce4c625f6"}, +] + [[package]] name = "mypy" version = "0.971" @@ -738,57 +849,57 @@ attrs = ">=19.2.0" [[package]] name = "packaging" -version = "23.0" +version = "23.1" description = "Core utilities for Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "pathspec" -version = "0.11.0" +version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, - {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] [[package]] name = "platformdirs" -version = "3.1.0" +version = "3.9.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"}, - {file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"}, + {file = "platformdirs-3.9.1-py3-none-any.whl", hash = "sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f"}, + {file = "platformdirs-3.9.1.tar.gz", hash = "sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421"}, ] [package.dependencies] -typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=4.6.3", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.2.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] [package.dependencies] @@ -800,83 +911,74 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "psycopg2-binary" -version = "2.9.5" +version = "2.9.6" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "main" optional = true python-versions = ">=3.6" files = [ - {file = "psycopg2-binary-2.9.5.tar.gz", hash = "sha256:33e632d0885b95a8b97165899006c40e9ecdc634a529dca7b991eb7de4ece41c"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:0775d6252ccb22b15da3b5d7adbbf8cfe284916b14b6dc0ff503a23edb01ee85"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3520d7af1ebc838cc6084a3281145d5cd5bdd43fdef139e6db5af01b92596cb7"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cbc554ba47ecca8cd3396ddaca85e1ecfe3e48dd57dc5e415e59551affe568e"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:5d28ecdf191db558d0c07d0f16524ee9d67896edf2b7990eea800abeb23ebd61"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:b9c33d4aef08dfecbd1736ceab8b7b3c4358bf10a0121483e5cd60d3d308cc64"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05b3d479425e047c848b9782cd7aac9c6727ce23181eb9647baf64ffdfc3da41"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1e491e6489a6cb1d079df8eaa15957c277fdedb102b6a68cfbf40c4994412fd0"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:9e32cedc389bcb76d9f24ea8a012b3cb8385ee362ea437e1d012ffaed106c17d"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46850a640df62ae940e34a163f72e26aca1f88e2da79148e1862faaac985c302"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-win32.whl", hash = "sha256:3d790f84201c3698d1bfb404c917f36e40531577a6dda02e45ba29b64d539867"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:1764546ffeaed4f9428707be61d68972eb5ede81239b46a45843e0071104d0dd"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_10_9_universal2.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:426c2ae999135d64e6a18849a7d1ad0e1bd007277e4a8f4752eaa40a96b550ff"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cf1d44e710ca3a9ce952bda2855830fe9f9017ed6259e01fcd71ea6287565f5"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024030b13bdcbd53d8a93891a2cf07719715724fc9fee40243f3bd78b4264b8f"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcda1c84a1c533c528356da5490d464a139b6e84eb77cc0b432e38c5c6dd7882"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:2ef892cabdccefe577088a79580301f09f2a713eb239f4f9f62b2b29cafb0577"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:af0516e1711995cb08dc19bbd05bec7dbdebf4185f68870595156718d237df3e"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e72c91bda9880f097c8aa3601a2c0de6c708763ba8128006151f496ca9065935"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e67b3c26e9b6d37b370c83aa790bbc121775c57bfb096c2e77eacca25fd0233b"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5fc447058d083b8c6ac076fc26b446d44f0145308465d745fba93a28c14c9e32"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d892bfa1d023c3781a3cab8dd5af76b626c483484d782e8bd047c180db590e4c"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-win32.whl", hash = "sha256:2abccab84d057723d2ca8f99ff7b619285d40da6814d50366f61f0fc385c3903"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:bef7e3f9dc6f0c13afdd671008534be5744e0e682fb851584c8c3a025ec09720"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:6e63814ec71db9bdb42905c925639f319c80e7909fb76c3b84edc79dadef8d60"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8a9bcab7b6db2e3dbf65b214dfc795b4c6b3bb3af922901b6a67f7cb47d5f8"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:56b2957a145f816726b109ee3d4e6822c23f919a7d91af5a94593723ed667835"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:f95b8aca2703d6a30249f83f4fe6a9abf2e627aa892a5caaab2267d56be7ab69"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:70831e03bd53702c941da1a1ad36c17d825a24fbb26857b40913d58df82ec18b"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:dbc332beaf8492b5731229a881807cd7b91b50dbbbaf7fe2faf46942eda64a24"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:2d964eb24c8b021623df1c93c626671420c6efadbdb8655cb2bd5e0c6fa422ba"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:95076399ec3b27a8f7fa1cc9a83417b1c920d55cf7a97f718a94efbb96c7f503"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-win32.whl", hash = "sha256:3fc33295cfccad697a97a76dec3f1e94ad848b7b163c3228c1636977966b51e2"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-win_amd64.whl", hash = "sha256:02551647542f2bf89073d129c73c05a25c372fc0a49aa50e0de65c3c143d8bd0"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:63e318dbe52709ed10d516a356f22a635e07a2e34c68145484ed96a19b0c4c68"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7e518a0911c50f60313cb9e74a169a65b5d293770db4770ebf004245f24b5c5"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d38a4656e4e715d637abdf7296e98d6267df0cc0a8e9a016f8ba07e4aa3eeb"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:68d81a2fe184030aa0c5c11e518292e15d342a667184d91e30644c9d533e53e1"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:7ee3095d02d6f38bd7d9a5358fcc9ea78fcdb7176921528dd709cc63f40184f5"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:46512486be6fbceef51d7660dec017394ba3e170299d1dc30928cbedebbf103a"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b911dfb727e247340d36ae20c4b9259e4a64013ab9888ccb3cbba69b77fd9636"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:422e3d43b47ac20141bc84b3d342eead8d8099a62881a501e97d15f6addabfe9"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c5682a45df7d9642eff590abc73157c887a68f016df0a8ad722dcc0f888f56d7"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-win32.whl", hash = "sha256:b8104f709590fff72af801e916817560dbe1698028cd0afe5a52d75ceb1fce5f"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:7b3751857da3e224f5629400736a7b11e940b5da5f95fa631d86219a1beaafec"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:043a9fd45a03858ff72364b4b75090679bd875ee44df9c0613dc862ca6b98460"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ffdc51001136b699f9563b1c74cc1f8c07f66ef7219beb6417a4c8aaa896c28"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15ba5982c177bc4b23a7940c7e4394197e2d6a424a2d282e7c236b66da6d896"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc85b3777068ed30aff8242be2813038a929f2084f69e43ef869daddae50f6ee"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:215d6bf7e66732a514f47614f828d8c0aaac9a648c46a831955cb103473c7147"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:7d07f552d1e412f4b4e64ce386d4c777a41da3b33f7098b6219012ba534fb2c2"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a0adef094c49f242122bb145c3c8af442070dc0e4312db17e49058c1702606d4"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:00475004e5ed3e3bf5e056d66e5dcdf41a0dc62efcd57997acd9135c40a08a50"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7d88db096fa19d94f433420eaaf9f3c45382da2dd014b93e4bf3215639047c16"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:902844f9c4fb19b17dfa84d9e2ca053d4a4ba265723d62ea5c9c26b38e0aa1e6"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-win32.whl", hash = "sha256:4e7904d1920c0c89105c0517dc7e3f5c20fb4e56ba9cdef13048db76947f1d79"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:a36a0e791805aa136e9cbd0ffa040d09adec8610453ee8a753f23481a0057af5"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:25382c7d174c679ce6927c16b6fbb68b10e56ee44b1acb40671e02d29f2fce7c"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9c38d3869238e9d3409239bc05bc27d6b7c99c2a460ea337d2814b35fb4fea1b"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c6527c8efa5226a9e787507652dd5ba97b62d29b53c371a85cd13f957fe4d42"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e59137cdb970249ae60be2a49774c6dfb015bd0403f05af1fe61862e9626642d"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:d4c7b3a31502184e856df1f7bbb2c3735a05a8ce0ade34c5277e1577738a5c91"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:b9a794cef1d9c1772b94a72eec6da144c18e18041d294a9ab47669bc77a80c1d"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5254cbd4f4855e11cebf678c1a848a3042d455a22a4ce61349c36aafd4c2267"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c5e65c6ac0ae4bf5bef1667029f81010b6017795dcb817ba5c7b8a8d61fab76f"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:74eddec4537ab1f701a1647214734bc52cee2794df748f6ae5908e00771f180a"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:01ad49d68dd8c5362e4bfb4158f2896dc6e0c02e87b8a3770fc003459f1a4425"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-win32.whl", hash = "sha256:937880290775033a743f4836aa253087b85e62784b63fd099ee725d567a48aa1"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:484405b883630f3e74ed32041a87456c5e0e63a8e3429aa93e8714c366d62bd1"}, + {file = "psycopg2-binary-2.9.6.tar.gz", hash = "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win32.whl", hash = "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win32.whl", hash = "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win32.whl", hash = "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win32.whl", hash = "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"}, ] [[package]] @@ -893,91 +995,90 @@ files = [ [[package]] name = "pycryptodomex" -version = "3.17" +version = "3.18.0" description = "Cryptographic library for Python" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ - {file = "pycryptodomex-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:12056c38e49d972f9c553a3d598425f8a1c1d35b2e4330f89d5ff1ffb70de041"}, - {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab33c2d9f275e05e235dbca1063753b5346af4a5cac34a51fa0da0d4edfb21d7"}, - {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:caa937ff29d07a665dfcfd7a84f0d4207b2ebf483362fa9054041d67fdfacc20"}, - {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:db23d7341e21b273d2440ec6faf6c8b1ca95c8894da612e165be0b89a8688340"}, - {file = "pycryptodomex-3.17-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f854c8476512cebe6a8681cc4789e4fcff6019c17baa0fd72b459155dc605ab4"}, - {file = "pycryptodomex-3.17-cp27-cp27m-win32.whl", hash = "sha256:a57e3257bacd719769110f1f70dd901c5b6955e9596ad403af11a3e6e7e3311c"}, - {file = "pycryptodomex-3.17-cp27-cp27m-win_amd64.whl", hash = "sha256:d38ab9e53b1c09608ba2d9b8b888f1e75d6f66e2787e437adb1fecbffec6b112"}, - {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:3c2516b42437ae6c7a29ef3ddc73c8d4714e7b6df995b76be4695bbe4b3b5cd2"}, - {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5c23482860302d0d9883404eaaa54b0615eefa5274f70529703e2c43cc571827"}, - {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:7a8dc3ee7a99aae202a4db52de5a08aa4d01831eb403c4d21da04ec2f79810db"}, - {file = "pycryptodomex-3.17-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:7cc28dd33f1f3662d6da28ead4f9891035f63f49d30267d3b41194c8778997c8"}, - {file = "pycryptodomex-3.17-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:2d4d395f109faba34067a08de36304e846c791808524614c731431ee048fe70a"}, - {file = "pycryptodomex-3.17-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:55eed98b4150a744920597c81b3965b632038781bab8a08a12ea1d004213c600"}, - {file = "pycryptodomex-3.17-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:7fa0b52df90343fafe319257b31d909be1d2e8852277fb0376ba89d26d2921db"}, - {file = "pycryptodomex-3.17-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78f0ddd4adc64baa39b416f3637aaf99f45acb0bcdc16706f0cc7ebfc6f10109"}, - {file = "pycryptodomex-3.17-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4fa037078e92c7cc49f6789a8bac3de06856740bb2038d05f2d9a2e4b165d59"}, - {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:88b0d5bb87eaf2a31e8a759302b89cf30c97f2f8ca7d83b8c9208abe8acb447a"}, - {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:6feedf4b0e36b395329b4186a805f60f900129cdf0170e120ecabbfcb763995d"}, - {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a6651a07f67c28b6e978d63aa3a3fccea0feefed9a8453af3f7421a758461b7"}, - {file = "pycryptodomex-3.17-cp35-abi3-win32.whl", hash = "sha256:32e764322e902bbfac49ca1446604d2839381bbbdd5a57920c9daaf2e0b778df"}, - {file = "pycryptodomex-3.17-cp35-abi3-win_amd64.whl", hash = "sha256:4b51e826f0a04d832eda0790bbd0665d9bfe73e5a4d8ea93b6a9b38beeebe935"}, - {file = "pycryptodomex-3.17-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:d4cf0128da167562c49b0e034f09e9cedd733997354f2314837c2fa461c87bb1"}, - {file = "pycryptodomex-3.17-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:c92537b596bd5bffb82f8964cabb9fef1bca8a28a9e0a69ffd3ec92a4a7ad41b"}, - {file = "pycryptodomex-3.17-pp27-pypy_73-win32.whl", hash = "sha256:599bb4ae4bbd614ca05f49bd4e672b7a250b80b13ae1238f05fd0f09d87ed80a"}, - {file = "pycryptodomex-3.17-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4c4674f4b040321055c596aac926d12f7f6859dfe98cd12f4d9453b43ab6adc8"}, - {file = "pycryptodomex-3.17-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a3648025e4ddb72d43addab764336ba2e670c8377dba5dd752e42285440d31"}, - {file = "pycryptodomex-3.17-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8a11f578bd0851b02719c862d55d3ee18d906c8b68a9c09f8c564d6bb5b92"}, - {file = "pycryptodomex-3.17-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:23d83b610bd97704f0cd3acc48d99b76a15c8c1540d8665c94d514a49905bad7"}, - {file = "pycryptodomex-3.17-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd29d35ac80755e5c0a99d96b44fb9abbd7e871849581ea6a4cb826d24267537"}, - {file = "pycryptodomex-3.17-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64b876d57cb894b31056ad8dd6a6ae1099b117ae07a3d39707221133490e5715"}, - {file = "pycryptodomex-3.17-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8bf4fdcad7d66beb744957db8717afc12d176e3fd9c5d106835133881a049b"}, - {file = "pycryptodomex-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c84689c73358dfc23f9fdcff2cb9e7856e65e2ce3b5ed8ff630d4c9bdeb1867b"}, - {file = "pycryptodomex-3.17.tar.gz", hash = "sha256:0af93aad8d62e810247beedef0261c148790c52f3cd33643791cc6396dd217c1"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win32.whl", hash = "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470"}, + {file = "pycryptodomex-3.18.0.tar.gz", hash = "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e"}, ] [[package]] name = "pydantic" -version = "1.10.5" +version = "1.10.11" description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5920824fe1e21cbb3e38cf0f3dd24857c8959801d1031ce1fac1d50857a03bfb"}, - {file = "pydantic-1.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3bb99cf9655b377db1a9e47fa4479e3330ea96f4123c6c8200e482704bf1eda2"}, - {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2185a3b3d98ab4506a3f6707569802d2d92c3a7ba3a9a35683a7709ea6c2aaa2"}, - {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f582cac9d11c227c652d3ce8ee223d94eb06f4228b52a8adaafa9fa62e73d5c9"}, - {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c9e5b778b6842f135902e2d82624008c6a79710207e28e86966cd136c621bfee"}, - {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72ef3783be8cbdef6bca034606a5de3862be6b72415dc5cb1fb8ddbac110049a"}, - {file = "pydantic-1.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:45edea10b75d3da43cfda12f3792833a3fa70b6eee4db1ed6aed528cef17c74e"}, - {file = "pydantic-1.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63200cd8af1af2c07964546b7bc8f217e8bda9d0a2ef0ee0c797b36353914984"}, - {file = "pydantic-1.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:305d0376c516b0dfa1dbefeae8c21042b57b496892d721905a6ec6b79494a66d"}, - {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd326aff5d6c36f05735c7c9b3d5b0e933b4ca52ad0b6e4b38038d82703d35b"}, - {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bb0452d7b8516178c969d305d9630a3c9b8cf16fcf4713261c9ebd465af0d73"}, - {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a9d9155e2a9f38b2eb9374c88f02fd4d6851ae17b65ee786a87d032f87008f8"}, - {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f836444b4c5ece128b23ec36a446c9ab7f9b0f7981d0d27e13a7c366ee163f8a"}, - {file = "pydantic-1.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:8481dca324e1c7b715ce091a698b181054d22072e848b6fc7895cd86f79b4449"}, - {file = "pydantic-1.10.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87f831e81ea0589cd18257f84386bf30154c5f4bed373b7b75e5cb0b5d53ea87"}, - {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1612e98c6326f10888df951a26ec1a577d8df49ddcaea87773bfbe23ba5cc"}, - {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58e41dd1e977531ac6073b11baac8c013f3cd8706a01d3dc74e86955be8b2c0c"}, - {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6a4b0aab29061262065bbdede617ef99cc5914d1bf0ddc8bcd8e3d7928d85bd6"}, - {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36e44a4de37b8aecffa81c081dbfe42c4d2bf9f6dff34d03dce157ec65eb0f15"}, - {file = "pydantic-1.10.5-cp37-cp37m-win_amd64.whl", hash = "sha256:261f357f0aecda005934e413dfd7aa4077004a174dafe414a8325e6098a8e419"}, - {file = "pydantic-1.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b429f7c457aebb7fbe7cd69c418d1cd7c6fdc4d3c8697f45af78b8d5a7955760"}, - {file = "pydantic-1.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:663d2dd78596c5fa3eb996bc3f34b8c2a592648ad10008f98d1348be7ae212fb"}, - {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51782fd81f09edcf265823c3bf43ff36d00db246eca39ee765ef58dc8421a642"}, - {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c428c0f64a86661fb4873495c4fac430ec7a7cef2b8c1c28f3d1a7277f9ea5ab"}, - {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:76c930ad0746c70f0368c4596020b736ab65b473c1f9b3872310a835d852eb19"}, - {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3257bd714de9db2102b742570a56bf7978e90441193acac109b1f500290f5718"}, - {file = "pydantic-1.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:f5bee6c523d13944a1fdc6f0525bc86dbbd94372f17b83fa6331aabacc8fd08e"}, - {file = "pydantic-1.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:532e97c35719f137ee5405bd3eeddc5c06eb91a032bc755a44e34a712420daf3"}, - {file = "pydantic-1.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca9075ab3de9e48b75fa8ccb897c34ccc1519177ad8841d99f7fd74cf43be5bf"}, - {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd46a0e6296346c477e59a954da57beaf9c538da37b9df482e50f836e4a7d4bb"}, - {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3353072625ea2a9a6c81ad01b91e5c07fa70deb06368c71307529abf70d23325"}, - {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f9d9b2be177c3cb6027cd67fbf323586417868c06c3c85d0d101703136e6b31"}, - {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b473d00ccd5c2061fd896ac127b7755baad233f8d996ea288af14ae09f8e0d1e"}, - {file = "pydantic-1.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:5f3bc8f103b56a8c88021d481410874b1f13edf6e838da607dcb57ecff9b4594"}, - {file = "pydantic-1.10.5-py3-none-any.whl", hash = "sha256:7c5b94d598c90f2f46b3a983ffb46ab806a67099d118ae0da7ef21a2a4033b28"}, - {file = "pydantic-1.10.5.tar.gz", hash = "sha256:9e337ac83686645a46db0e825acceea8e02fca4062483f40e9ae178e8bd1103a"}, + {file = "pydantic-1.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ff44c5e89315b15ff1f7fdaf9853770b810936d6b01a7bcecaa227d2f8fe444f"}, + {file = "pydantic-1.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c098d4ab5e2d5b3984d3cb2527e2d6099d3de85630c8934efcfdc348a9760e"}, + {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16928fdc9cb273c6af00d9d5045434c39afba5f42325fb990add2c241402d151"}, + {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0588788a9a85f3e5e9ebca14211a496409cb3deca5b6971ff37c556d581854e7"}, + {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9baf78b31da2dc3d3f346ef18e58ec5f12f5aaa17ac517e2ffd026a92a87588"}, + {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:373c0840f5c2b5b1ccadd9286782852b901055998136287828731868027a724f"}, + {file = "pydantic-1.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:c3339a46bbe6013ef7bdd2844679bfe500347ac5742cd4019a88312aa58a9847"}, + {file = "pydantic-1.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08a6c32e1c3809fbc49debb96bf833164f3438b3696abf0fbeceb417d123e6eb"}, + {file = "pydantic-1.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a451ccab49971af043ec4e0d207cbc8cbe53dbf148ef9f19599024076fe9c25b"}, + {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b02d24f7b2b365fed586ed73582c20f353a4c50e4be9ba2c57ab96f8091ddae"}, + {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f34739a89260dfa420aa3cbd069fbcc794b25bbe5c0a214f8fb29e363484b66"}, + {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e297897eb4bebde985f72a46a7552a7556a3dd11e7f76acda0c1093e3dbcf216"}, + {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d185819a7a059550ecb85d5134e7d40f2565f3dd94cfd870132c5f91a89cf58c"}, + {file = "pydantic-1.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:4400015f15c9b464c9db2d5d951b6a780102cfa5870f2c036d37c23b56f7fc1b"}, + {file = "pydantic-1.10.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2417de68290434461a266271fc57274a138510dca19982336639484c73a07af6"}, + {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:331c031ba1554b974c98679bd0780d89670d6fd6f53f5d70b10bdc9addee1713"}, + {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8268a735a14c308923e8958363e3a3404f6834bb98c11f5ab43251a4e410170c"}, + {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:44e51ba599c3ef227e168424e220cd3e544288c57829520dc90ea9cb190c3248"}, + {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d7781f1d13b19700b7949c5a639c764a077cbbdd4322ed505b449d3ca8edcb36"}, + {file = "pydantic-1.10.11-cp37-cp37m-win_amd64.whl", hash = "sha256:7522a7666157aa22b812ce14c827574ddccc94f361237ca6ea8bb0d5c38f1629"}, + {file = "pydantic-1.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc64eab9b19cd794a380179ac0e6752335e9555d214cfcb755820333c0784cb3"}, + {file = "pydantic-1.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8dc77064471780262b6a68fe67e013298d130414d5aaf9b562c33987dbd2cf4f"}, + {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe429898f2c9dd209bd0632a606bddc06f8bce081bbd03d1c775a45886e2c1cb"}, + {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:192c608ad002a748e4a0bed2ddbcd98f9b56df50a7c24d9a931a8c5dd053bd3d"}, + {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ef55392ec4bb5721f4ded1096241e4b7151ba6d50a50a80a2526c854f42e6a2f"}, + {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e0bb6efe86281623abbeeb0be64eab740c865388ee934cd3e6a358784aca6e"}, + {file = "pydantic-1.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:265a60da42f9f27e0b1014eab8acd3e53bd0bad5c5b4884e98a55f8f596b2c19"}, + {file = "pydantic-1.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:469adf96c8e2c2bbfa655fc7735a2a82f4c543d9fee97bd113a7fb509bf5e622"}, + {file = "pydantic-1.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e6cbfbd010b14c8a905a7b10f9fe090068d1744d46f9e0c021db28daeb8b6de1"}, + {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abade85268cc92dff86d6effcd917893130f0ff516f3d637f50dadc22ae93999"}, + {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9738b0f2e6c70f44ee0de53f2089d6002b10c33264abee07bdb5c7f03038303"}, + {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:787cf23e5a0cde753f2eabac1b2e73ae3844eb873fd1f5bdbff3048d8dbb7604"}, + {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:174899023337b9fc685ac8adaa7b047050616136ccd30e9070627c1aaab53a13"}, + {file = "pydantic-1.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:1954f8778489a04b245a1e7b8b22a9d3ea8ef49337285693cf6959e4b757535e"}, + {file = "pydantic-1.10.11-py3-none-any.whl", hash = "sha256:008c5e266c8aada206d0627a011504e14268a62091450210eda7c07fabe6963e"}, + {file = "pydantic-1.10.11.tar.gz", hash = "sha256:f66d479cf7eb331372c470614be6511eae96f1f120344c25f3f9bb59fb1b5528"}, ] [package.dependencies] @@ -1045,14 +1146,14 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy [[package]] name = "pytest-cov" -version = "4.0.0" +version = "4.1.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, - {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] @@ -1109,21 +1210,21 @@ test = ["ipython", "mock", "pytest (>=3.0.5)"] [[package]] name = "requests" -version = "2.28.2" +version = "2.31.0" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] @@ -1335,66 +1436,84 @@ files = [ [[package]] name = "typed-ast" -version = "1.5.4" +version = "1.5.5" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, + {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, + {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, + {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, + {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, + {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, + {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, + {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, + {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, + {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, ] [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" -version = "1.26.14" +version = "2.0.3" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, - {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, + {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, + {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" @@ -1418,14 +1537,14 @@ standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", [[package]] name = "websocket-client" -version = "1.5.1" +version = "1.6.1" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, + {file = "websocket-client-1.6.1.tar.gz", hash = "sha256:c951af98631d24f8df89ab1019fc365f2227c0892f12fd150e935607c79dd0dd"}, + {file = "websocket_client-1.6.1-py3-none-any.whl", hash = "sha256:f1f9f2ad5291f0225a49efad77abf9e700b6fef553900623060dad6e26503b9d"}, ] [package.extras] @@ -1485,4 +1604,4 @@ pgsql = ["psycopg2-binary"] [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "b33d82f1d4b58a39ab9fe6c887cf88a3246e9c99dc7bfe5df93923ad00d79814" +content-hash = "22155a0a398b0cb50e8df08d43c4482aeffcc2e2cfcf2827b2e1daa2c30d8755" diff --git a/pyproject.toml b/pyproject.toml index 8c01a45..26ba0bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ wheel = "^0.38.4" importlib-metadata = "^5.2.0" psycopg2-binary = {version = "^2.9.5", optional = true } httpx = "0.23.0" +bip32 = "^3.4" +mnemonic = "^0.20" [tool.poetry.extras] pgsql = ["psycopg2-binary"] diff --git a/requirements.txt b/requirements.txt index 5204a4e..96271c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,38 +1,39 @@ -anyio==3.6.2 ; python_version >= "3.7" and python_version < "4.0" -attrs==22.2.0 ; python_version >= "3.7" and python_version < "4.0" +anyio==3.7.1 ; python_version >= "3.7" and python_version < "4.0" +asn1crypto==1.5.1 ; python_version >= "3.7" and python_version < "4.0" +attrs==23.1.0 ; python_version >= "3.7" and python_version < "4.0" +base58==2.1.1 ; python_version >= "3.7" and python_version < "4.0" bech32==1.2.0 ; python_version >= "3.7" and python_version < "4.0" +bip32==3.4 ; python_version >= "3.7" and python_version < "4.0" bitstring==3.1.9 ; python_version >= "3.7" and python_version < "4.0" -certifi==2022.12.7 ; python_version >= "3.7" and python_version < "4" +certifi==2023.5.7 ; python_version >= "3.7" and python_version < "4.0" cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0" -charset-normalizer==3.0.1 ; python_version >= "3.7" and python_version < "4" -click==8.1.3 ; python_version >= "3.7" and python_version < "4.0" +charset-normalizer==3.2.0 ; python_version >= "3.7" and python_version < "4.0" +click==8.1.5 ; python_version >= "3.7" and python_version < "4.0" +coincurve==18.0.0 ; python_version >= "3.7" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows" or python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32" cryptography==36.0.2 ; python_version >= "3.7" and python_version < "4.0" ecdsa==0.18.0 ; python_version >= "3.7" and python_version < "4.0" environs==9.5.0 ; python_version >= "3.7" and python_version < "4.0" -exceptiongroup==1.1.0 ; python_version >= "3.7" and python_version < "3.11" +exceptiongroup==1.1.2 ; python_version >= "3.7" and python_version < "3.11" fastapi==0.83.0 ; python_version >= "3.7" and python_version < "4.0" h11==0.12.0 ; python_version >= "3.7" and python_version < "4.0" httpcore==0.15.0 ; python_version >= "3.7" and python_version < "4.0" httpx==0.23.0 ; python_version >= "3.7" and python_version < "4.0" -idna==3.4 ; python_version >= "3.7" and python_version < "4" +idna==3.4 ; python_version >= "3.7" and python_version < "4.0" importlib-metadata==5.2.0 ; python_version >= "3.7" and python_version < "4.0" -iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "4.0" loguru==0.6.0 ; python_version >= "3.7" and python_version < "4.0" marshmallow==3.19.0 ; python_version >= "3.7" and python_version < "4.0" +mnemonic==0.20 ; python_version >= "3.7" and python_version < "4.0" outcome==1.2.0 ; python_version >= "3.7" and python_version < "4.0" -packaging==23.0 ; python_version >= "3.7" and python_version < "4.0" -pluggy==1.0.0 ; python_version >= "3.7" and python_version < "4.0" +packaging==23.1 ; python_version >= "3.7" and python_version < "4.0" pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0" -pycryptodomex==3.17 ; python_version >= "3.7" and python_version < "4.0" -pydantic==1.10.5 ; python_version >= "3.7" and python_version < "4.0" +pycryptodomex==3.18.0 ; python_version >= "3.7" and python_version < "4.0" +pydantic==1.10.11 ; python_version >= "3.7" and python_version < "4.0" pysocks==1.7.1 ; python_version >= "3.7" and python_version < "4.0" -pytest-asyncio==0.19.0 ; python_version >= "3.7" and python_version < "4.0" -pytest==7.2.2 ; python_version >= "3.7" and python_version < "4.0" python-bitcoinlib==0.11.2 ; python_version >= "3.7" and python_version < "4.0" python-dotenv==0.21.1 ; python_version >= "3.7" and python_version < "4.0" represent==1.6.0.post0 ; python_version >= "3.7" and python_version < "4.0" -requests==2.28.2 ; python_version >= "3.7" and python_version < "4" +requests==2.31.0 ; python_version >= "3.7" and python_version < "4.0" rfc3986[idna2008]==1.5.0 ; python_version >= "3.7" and python_version < "4.0" secp256k1==0.14.0 ; python_version >= "3.7" and python_version < "4.0" setuptools==65.7.0 ; python_version >= "3.7" and python_version < "4.0" @@ -41,11 +42,10 @@ sniffio==1.3.0 ; python_version >= "3.7" and python_version < "4.0" sqlalchemy-aio==0.17.0 ; python_version >= "3.7" and python_version < "4.0" sqlalchemy==1.3.24 ; python_version >= "3.7" and python_version < "4.0" starlette==0.19.1 ; python_version >= "3.7" and python_version < "4.0" -tomli==2.0.1 ; python_version >= "3.7" and python_version < "3.11" -typing-extensions==4.5.0 ; python_version >= "3.7" and python_version < "4.0" -urllib3==1.26.14 ; python_version >= "3.7" and python_version < "4" +typing-extensions==4.7.1 ; python_version >= "3.7" and python_version < "4.0" +urllib3==2.0.3 ; python_version >= "3.7" and python_version < "4.0" uvicorn==0.18.3 ; python_version >= "3.7" and python_version < "4.0" -websocket-client==1.5.1 ; python_version >= "3.7" and python_version < "4.0" +websocket-client==1.6.1 ; python_version >= "3.7" and python_version < "4.0" wheel==0.38.4 ; python_version >= "3.7" and python_version < "4.0" win32-setctime==1.1.0 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32" zipp==3.15.0 ; python_version >= "3.7" and python_version < "4.0" diff --git a/setup.py b/setup.py index d4ebc5e..d1d5ea6 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli.cli:cli"]} setuptools.setup( name="cashu", - version="0.12.3", + version="0.13.0", description="Ecash wallet and mint for Bitcoin Lightning", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/conftest.py b/tests/conftest.py index 642147f..cdd0905 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,7 +37,6 @@ class UvicornServer(multiprocessing.Process): def run(self, *args, **kwargs): settings.lightning = False settings.mint_lightning_backend = "FakeWallet" - settings.mint_listen_port = 3337 settings.mint_database = "data/test_mint" settings.mint_private_key = self.private_key settings.mint_derivation_path = "0/0/0/0" @@ -46,36 +45,24 @@ class UvicornServer(multiprocessing.Process): if dirpath.exists() and dirpath.is_dir(): shutil.rmtree(dirpath) - dirpath = Path("data/test_wallet") + dirpath = Path("data/wallet1") + if dirpath.exists() and dirpath.is_dir(): + shutil.rmtree(dirpath) + + dirpath = Path("data/wallet2") + if dirpath.exists() and dirpath.is_dir(): + shutil.rmtree(dirpath) + + dirpath = Path("data/wallet3") if dirpath.exists() and dirpath.is_dir(): shutil.rmtree(dirpath) self.server.run() -@pytest.fixture(autouse=True, scope="session") -def mint(): - settings.mint_listen_port = 3337 - settings.port = 3337 - settings.mint_url = "http://localhost:3337" - settings.port = settings.mint_listen_port - settings.nostr_private_key = secrets.token_hex(32) # wrong private key - config = uvicorn.Config( - "cashu.mint.app:app", - port=settings.mint_listen_port, - host="127.0.0.1", - ) - - server = UvicornServer(config=config) - server.start() - time.sleep(1) - yield server - server.stop() - - @pytest_asyncio.fixture(scope="function") async def ledger(): - async def start_mint_init(ledger): + async def start_mint_init(ledger: Ledger): await migrate_databases(ledger.db, migrations_mint) await ledger.load_used_proofs() await ledger.init_keysets() @@ -93,20 +80,18 @@ async def ledger(): yield ledger -# @pytest.fixture(autouse=True, scope="session") -# def mint_3338(): -# settings.mint_listen_port = 3338 -# settings.port = 3338 -# settings.mint_url = "http://localhost:3338" -# settings.port = settings.mint_listen_port -# config = uvicorn.Config( -# "cashu.mint.app:app", -# port=settings.mint_listen_port, -# host="127.0.0.1", -# ) +@pytest.fixture(autouse=True, scope="session") +def mint(): + settings.mint_listen_port = 3337 + settings.mint_url = "http://localhost:3337" + config = uvicorn.Config( + "cashu.mint.app:app", + port=settings.mint_listen_port, + host="127.0.0.1", + ) -# server = UvicornServer(config=config, private_key="SECOND_PRIVATE_KEY") -# server.start() -# time.sleep(1) -# yield server -# server.stop() + server = UvicornServer(config=config) + server.start() + time.sleep(1) + yield server + server.stop() diff --git a/tests/test_cli.py b/tests/test_cli.py index e3d0828..dc8b82d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -13,20 +13,15 @@ from tests.conftest import SERVER_ENDPOINT, mint @pytest.fixture(autouse=True, scope="session") def cli_prefix(): - yield ["--wallet", "test_wallet", "--host", settings.mint_url] - - -@pytest.fixture(scope="session") -def wallet(): - wallet = Wallet(settings.mint_host, "data/test_wallet", "wallet") - asyncio.run(migrate_databases(wallet.db, migrations)) - asyncio.run(wallet.load_proofs()) - yield wallet + yield ["--wallet", "test_cli_wallet", "--host", settings.mint_url, "--tests"] async def init_wallet(): - wallet = Wallet(settings.mint_host, "data/test_wallet", "wallet") - await migrate_databases(wallet.db, migrations) + wallet = await Wallet.with_db( + url=settings.mint_host, + db="data/test_cli_wallet", + name="wallet", + ) await wallet.load_proofs() return wallet @@ -50,7 +45,7 @@ def test_info_with_mint(cli_prefix): runner = CliRunner() result = runner.invoke( cli, - [*cli_prefix, "info", "-m"], + [*cli_prefix, "info", "--mint"], ) assert result.exception is None print("INFO -M") @@ -59,6 +54,20 @@ def test_info_with_mint(cli_prefix): assert result.exit_code == 0 +@pytest.mark.asyncio +def test_info_with_mnemonic(cli_prefix): + runner = CliRunner() + result = runner.invoke( + cli, + [*cli_prefix, "info", "--mnemonic"], + ) + assert result.exception is None + print("INFO -M") + print(result.output) + assert "Mnemonic" in result.output + assert result.exit_code == 0 + + @pytest.mark.asyncio def test_balance(cli_prefix): runner = CliRunner() @@ -113,7 +122,7 @@ def test_wallets(cli_prefix): print("WALLETS") # on github this is empty if len(result.output): - assert "test_wallet" in result.output + assert "test_cli_wallet" in result.output assert result.exit_code == 0 diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 2e196c3..2628b16 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -43,9 +43,11 @@ def test_step1(): """""" B_, blinding_factor = step1_alice( "test_message", - blinding_factor=bytes.fromhex( - "0000000000000000000000000000000000000000000000000000000000000001" - ), # 32 bytes + blinding_factor=PrivateKey( + privkey=bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000001" + ) # 32 bytes + ), ) assert ( @@ -60,9 +62,12 @@ def test_step1(): def test_step2(): B_, _ = step1_alice( "test_message", - blinding_factor=bytes.fromhex( - "0000000000000000000000000000000000000000000000000000000000000001" - ), # 32 bytes + blinding_factor=PrivateKey( + privkey=bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000001" + ), + raw=True, + ), ) a = PrivateKey( privkey=bytes.fromhex( diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 9634170..12f8782 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1,10 +1,12 @@ import asyncio -import copy -import secrets -from typing import List +import shutil +import time +from pathlib import Path +from typing import Dict, List import pytest import pytest_asyncio +from mnemonic import Mnemonic from cashu.core.base import Proof, Secret, SecretKind, Tags from cashu.core.crypto.secp import PrivateKey, PublicKey @@ -34,10 +36,20 @@ def assert_amt(proofs: List[Proof], expected: int): assert [p.amount for p in proofs] == expected +async def reset_wallet_db(wallet: Wallet): + await wallet.db.execute("DELETE FROM proofs") + await wallet.db.execute("DELETE FROM proofs_used") + await wallet.db.execute("DELETE FROM keysets") + await wallet._load_mint() + + @pytest_asyncio.fixture(scope="function") async def wallet1(mint): - wallet1 = Wallet1(SERVER_ENDPOINT, "data/wallet1", "wallet1") - await migrate_databases(wallet1.db, migrations) + wallet1 = await Wallet1.with_db( + url=SERVER_ENDPOINT, + db="data/wallet1", + name="wallet1", + ) await wallet1.load_mint() wallet1.status() yield wallet1 @@ -45,14 +57,34 @@ async def wallet1(mint): @pytest_asyncio.fixture(scope="function") async def wallet2(mint): - wallet2 = Wallet2(SERVER_ENDPOINT, "data/wallet2", "wallet2") - await migrate_databases(wallet2.db, migrations) - wallet2.private_key = PrivateKey(secrets.token_bytes(32), raw=True) + wallet2 = await Wallet2.with_db( + url=SERVER_ENDPOINT, + db="data/wallet2", + name="wallet2", + ) await wallet2.load_mint() wallet2.status() yield wallet2 +@pytest_asyncio.fixture(scope="function") +async def wallet3(mint): + dirpath = Path("data/wallet3") + if dirpath.exists() and dirpath.is_dir(): + shutil.rmtree(dirpath) + + wallet3 = await Wallet1.with_db( + url=SERVER_ENDPOINT, + db="data/wallet3", + name="wallet3", + ) + await wallet3.db.execute("DELETE FROM proofs") + await wallet3.db.execute("DELETE FROM proofs_used") + await wallet3.load_mint() + wallet3.status() + yield wallet3 + + @pytest.mark.asyncio async def test_get_keys(wallet1: Wallet): assert wallet1.keys.public_keys @@ -277,10 +309,283 @@ async def test_p2sh_receive_with_wrong_wallet(wallet1: Wallet, wallet2: Wallet): await assert_err(wallet2.redeem(send_proofs), "lock not found.") # wrong receiver -@pytest.mark.asyncio async def test_token_state(wallet1: Wallet): await wallet1.mint(64) assert wallet1.balance == 64 resp = await wallet1.check_proof_state(wallet1.proofs) assert resp.dict()["spendable"] assert resp.dict()["pending"] + + +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" + ) + 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 + assert [r.private_key for r in rs1] == [r.private_key for r in rs2] + assert derivaion_paths1 == derivaion_paths2 + assert secrets1 == [ + "9bfb12704297fe90983907d122838940755fcce370ce51e9e00a4275a347c3fe", + "dbc5e05f2b1f24ec0e2ab6e8312d5e13f57ada52594d4caf429a697d9c742490", + "06a29fa8081b3a620b50b473fc80cde9a575c3b94358f3513c03007f8b66321e", + "652d08c804bd2c5f2c1f3e3d8895860397df394b30473753227d766affd15e89", + "654e5997f8a20402f7487296b6f7e463315dd52fc6f6cc5a4e35c7f6ccac77e0", + ] + assert derivaion_paths1 == [ + "m/129372'/0'/2004500376'/0'", + "m/129372'/0'/2004500376'/1'", + "m/129372'/0'/2004500376'/2'", + "m/129372'/0'/2004500376'/3'", + "m/129372'/0'/2004500376'/4'", + ] + + +@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" + ) + 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 + rs1 = rs1_1 + rs1_2 + secrets2, rs2, derivaion_paths = await wallet3.generate_secrets_from_to(0, 4) + assert secrets1 == secrets2 + assert [r.private_key for r in rs1] == [r.private_key for r in rs2] + + +@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" + ) + 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) + assert len(secrets2) == 3 + assert secrets1[2:] == secrets2 + assert [r.private_key for r in rs1[2:]] == [r.private_key for r in rs2] + + +@pytest.mark.asyncio +async def test_restore_wallet_after_mint(wallet3: Wallet): + await reset_wallet_db(wallet3) + await wallet3.mint(64) + assert wallet3.balance == 64 + await reset_wallet_db(wallet3) + await wallet3.load_proofs() + wallet3.proofs = [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 20) + assert wallet3.balance == 64 + + +@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" + ), + "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 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 + ) + + await reset_wallet_db(wallet3) + await wallet3.load_proofs() + wallet3.proofs = [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 100) + assert wallet3.balance == 64 * 2 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 64 + + +@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 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 + ) + + await wallet2.redeem(spendable_proofs) + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 100) + assert wallet3.balance == 64 + 2 * 32 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 32 + + +class ProofBox: + proofs: Dict[str, Proof] = {} + + def add(self, proofs: List[Proof]) -> None: + for proof in proofs: + if proof.secret in self.proofs: + if self.proofs[proof.secret].C != proof.C: + print("Proofs are not equal") + print(self.proofs[proof.secret]) + print(proof) + else: + self.proofs[proof.secret] = proof + + +@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 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 + ) + + await wallet3.redeem(spendable_proofs) + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 100) + assert wallet3.balance == 64 + 2 * 32 + 32 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 64 + + +@pytest.mark.asyncio +async def test_restore_wallet_after_send_twice( + wallet3: Wallet, +): + box = ProofBox() + wallet3.private_key = PrivateKey() + await reset_wallet_db(wallet3) + + await wallet3.mint(2) + 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 + ) + box.add(wallet3.proofs) + assert wallet3.available_balance == 1 + await wallet3.redeem(spendable_proofs) + box.add(wallet3.proofs) + assert wallet3.available_balance == 2 + assert wallet3.balance == 2 + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 10) + box.add(wallet3.proofs) + assert wallet3.balance == 5 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 2 + + # again + + _, spendable_proofs = await wallet3.split_to_send( # type: ignore + wallet3.proofs, 1, set_reserved=True + ) + box.add(wallet3.proofs) + + assert wallet3.available_balance == 1 + await wallet3.redeem(spendable_proofs) + box.add(wallet3.proofs) + assert wallet3.available_balance == 2 + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 15) + box.add(wallet3.proofs) + assert wallet3.balance == 7 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 2 + + +@pytest.mark.asyncio +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 reset_wallet_db(wallet3) + + await wallet3.mint(64) + 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 + ) + box.add(wallet3.proofs) + + assert wallet3.available_balance == 64 - 10 + await wallet3.redeem(spendable_proofs) + box.add(wallet3.proofs) + assert wallet3.available_balance == 64 + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 20) + box.add(wallet3.proofs) + assert wallet3.balance == 138 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 64 + + # again + + _, spendable_proofs = await wallet3.split_to_send( # type: ignore + wallet3.proofs, 12, set_reserved=True + ) + + assert wallet3.available_balance == 64 - 12 + await wallet3.redeem(spendable_proofs) + assert wallet3.available_balance == 64 + + await reset_wallet_db(wallet3) + await wallet3.load_proofs(reload=True) + assert wallet3.proofs == [] + assert wallet3.balance == 0 + await wallet3.restore_promises(0, 50) + assert wallet3.balance == 182 + await wallet3.invalidate(wallet3.proofs) + assert wallet3.balance == 64 diff --git a/tests/test_wallet_api.py b/tests/test_wallet_api.py index b5757fe..6bd13ae 100644 --- a/tests/test_wallet_api.py +++ b/tests/test_wallet_api.py @@ -14,8 +14,11 @@ from tests.conftest import SERVER_ENDPOINT, mint @pytest_asyncio.fixture(scope="function") async def wallet(mint): - wallet = Wallet(SERVER_ENDPOINT, "data/test_wallet_api", "wallet_api") - await migrate_databases(wallet.db, migrations) + wallet = await Wallet.with_db( + url=SERVER_ENDPOINT, + db="data/test_wallet_api", + name="wallet_api", + ) await wallet.load_mint() wallet.status() yield wallet diff --git a/tests/test_wallet_p2pk.py b/tests/test_wallet_p2pk.py index c3197c5..a0a01b7 100644 --- a/tests/test_wallet_p2pk.py +++ b/tests/test_wallet_p2pk.py @@ -36,7 +36,7 @@ def assert_amt(proofs: List[Proof], expected: int): @pytest_asyncio.fixture(scope="function") async def wallet1(mint): - wallet1 = Wallet1(SERVER_ENDPOINT, "data/wallet_p2pk_1", "wallet1") + wallet1 = await Wallet1.with_db(SERVER_ENDPOINT, "data/wallet_p2pk_1", "wallet1") await migrate_databases(wallet1.db, migrations) await wallet1.load_mint() wallet1.status() @@ -45,7 +45,7 @@ async def wallet1(mint): @pytest_asyncio.fixture(scope="function") async def wallet2(mint): - wallet2 = Wallet2(SERVER_ENDPOINT, "data/wallet_p2pk_2", "wallet2") + wallet2 = await Wallet2.with_db(SERVER_ENDPOINT, "data/wallet_p2pk_2", "wallet2") await migrate_databases(wallet2.db, migrations) wallet2.private_key = PrivateKey(secrets.token_bytes(32), raw=True) await wallet2.load_mint() diff --git a/tests/test_wallet_p2sh.py b/tests/test_wallet_p2sh.py index 4079fe3..7d4724f 100644 --- a/tests/test_wallet_p2sh.py +++ b/tests/test_wallet_p2sh.py @@ -36,7 +36,7 @@ def assert_amt(proofs: List[Proof], expected: int): @pytest_asyncio.fixture(scope="function") async def wallet1(mint): - wallet1 = Wallet1(SERVER_ENDPOINT, "data/wallet_p2sh_1", "wallet1") + wallet1 = await Wallet1.with_db(SERVER_ENDPOINT, "data/wallet_p2sh_1", "wallet1") await migrate_databases(wallet1.db, migrations) await wallet1.load_mint() wallet1.status() @@ -45,7 +45,7 @@ async def wallet1(mint): @pytest_asyncio.fixture(scope="function") async def wallet2(mint): - wallet2 = Wallet2(SERVER_ENDPOINT, "data/wallet_p2sh_2", "wallet2") + wallet2 = await Wallet2.with_db(SERVER_ENDPOINT, "data/wallet_p2sh_2", "wallet2") await migrate_databases(wallet2.db, migrations) wallet2.private_key = PrivateKey(secrets.token_bytes(32), raw=True) await wallet2.load_mint()