index on db and read spent proofs from db (#370)

* index on db and read spent proofs from db

* add benchmark for testing

* remove benchmark

* add option to disable cached secrets

* disable python 3.9 tests
This commit is contained in:
callebtc
2023-11-26 06:07:38 -03:00
committed by GitHub
parent bff30d493d
commit fa5193cd8f
14 changed files with 253 additions and 164 deletions

View File

@@ -12,8 +12,9 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
python-version: ["3.9", "3.10"] python-version: ["3.10"]
poetry-version: ["1.5.1"] poetry-version: ["1.5.1"]
mint-cache-secrets: ["true", "false"]
# db-url: ["", "postgres://cashu:cashu@localhost:5432/test"] # TODO: Postgres test not working # db-url: ["", "postgres://cashu:cashu@localhost:5432/test"] # TODO: Postgres test not working
db-url: [""] db-url: [""]
backend-wallet-class: ["FakeWallet"] backend-wallet-class: ["FakeWallet"]
@@ -21,6 +22,7 @@ jobs:
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
poetry-version: ${{ matrix.poetry-version }} poetry-version: ${{ matrix.poetry-version }}
mint-cache-secrets: ${{ matrix.mint-cache-secrets }}
regtest: regtest:
uses: ./.github/workflows/regtest.yml uses: ./.github/workflows/regtest.yml
strategy: strategy:

View File

@@ -15,6 +15,9 @@ on:
os: os:
default: "ubuntu-latest" default: "ubuntu-latest"
type: string type: string
mint-cache-secrets:
default: "false"
type: string
jobs: jobs:
poetry: poetry:
@@ -47,6 +50,7 @@ jobs:
MINT_HOST: localhost MINT_HOST: localhost
MINT_PORT: 3337 MINT_PORT: 3337
MINT_DATABASE: ${{ inputs.db-url }} MINT_DATABASE: ${{ inputs.db-url }}
MINT_CACHE_SECRETS: ${{ inputs.mint-cache-secrets }}
TOR: false TOR: false
run: | run: |
make test make test

View File

@@ -5,6 +5,7 @@ import time
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import Optional, Union from typing import Optional, Union
from loguru import logger
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy_aio.base import AsyncConnection from sqlalchemy_aio.base import AsyncConnection
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
@@ -130,7 +131,7 @@ class Database(Compat):
# ) # )
else: else:
if not os.path.exists(self.db_location): if not os.path.exists(self.db_location):
print(f"Creating database directory: {self.db_location}") logger.info(f"Creating database directory: {self.db_location}")
os.makedirs(self.db_location) os.makedirs(self.db_location)
self.path = os.path.join(self.db_location, f"{self.name}.sqlite3") self.path = os.path.join(self.db_location, f"{self.name}.sqlite3")
database_uri = f"sqlite:///{self.path}" database_uri = f"sqlite:///{self.path}"

View File

@@ -44,6 +44,7 @@ class EnvSettings(CashuSettings):
debug: bool = Field(default=False) debug: bool = Field(default=False)
log_level: str = Field(default="INFO") log_level: str = Field(default="INFO")
cashu_dir: str = Field(default=os.path.join(str(Path.home()), ".cashu")) cashu_dir: str = Field(default=os.path.join(str(Path.home()), ".cashu"))
debug_profiling: bool = Field(default=False)
class MintSettings(CashuSettings): class MintSettings(CashuSettings):
@@ -61,6 +62,8 @@ class MintSettings(CashuSettings):
mint_lnbits_endpoint: str = Field(default=None) mint_lnbits_endpoint: str = Field(default=None)
mint_lnbits_key: str = Field(default=None) mint_lnbits_key: str = Field(default=None)
mint_cache_secrets: bool = Field(default=True)
class MintInformation(CashuSettings): class MintInformation(CashuSettings):
mint_info_name: str = Field(default="Cashu mint") mint_info_name: str = Field(default="Cashu mint")

View File

@@ -8,8 +8,6 @@ from fastapi.exception_handlers import (
) )
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
# from fastapi_profiler import PyInstrumentProfilerMiddleware
from loguru import logger from loguru import logger
from starlette.middleware import Middleware from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware from starlette.middleware.cors import CORSMiddleware
@@ -20,6 +18,9 @@ from ..core.settings import settings
from .router import router from .router import router
from .startup import start_mint_init from .startup import start_mint_init
if settings.debug_profiling:
from fastapi_profiler import PyInstrumentProfilerMiddleware
# from starlette_context import context # from starlette_context import context
# from starlette_context.middleware import RawContextMiddleware # from starlette_context.middleware import RawContextMiddleware
@@ -108,7 +109,9 @@ def create_app(config_object="core.settings") -> FastAPI:
middleware=middleware, middleware=middleware,
) )
# app.add_middleware(PyInstrumentProfilerMiddleware) if settings.debug_profiling:
assert PyInstrumentProfilerMiddleware is not None
app.add_middleware(PyInstrumentProfilerMiddleware)
return app return app

View File

@@ -31,7 +31,7 @@ class LedgerCrud:
db: Database, db: Database,
id: str, id: str,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
): ) -> Optional[Invoice]:
return await get_lightning_invoice( return await get_lightning_invoice(
db=db, db=db,
id=id, id=id,
@@ -42,8 +42,23 @@ class LedgerCrud:
self, self,
db: Database, db: Database,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
): ) -> List[str]:
return await get_secrets_used(db=db, conn=conn) return await get_secrets_used(
db=db,
conn=conn,
)
async def get_proof_used(
self,
db: Database,
proof: Proof,
conn: Optional[Connection] = None,
) -> Optional[Proof]:
return await get_proof_used(
db=db,
proof=proof,
conn=conn,
)
async def invalidate_proof( async def invalidate_proof(
self, self,
@@ -215,7 +230,7 @@ async def get_promise(
async def get_secrets_used( async def get_secrets_used(
db: Database, db: Database,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
): ) -> List[str]:
rows = await (conn or db).fetchall(f""" rows = await (conn or db).fetchall(f"""
SELECT secret from {table_with_schema(db, 'proofs_used')} SELECT secret from {table_with_schema(db, 'proofs_used')}
""") """)
@@ -253,6 +268,21 @@ async def get_proofs_pending(
return [Proof(**r) for r in rows] return [Proof(**r) for r in rows]
async def get_proof_used(
db: Database,
proof: Proof,
conn: Optional[Connection] = None,
) -> Optional[Proof]:
row = await (conn or db).fetchone(
f"""
SELECT 1 from {table_with_schema(db, 'proofs_used')}
WHERE secret = ?
""",
(str(proof.secret),),
)
return Proof(**row) if row else None
async def set_proof_pending( async def set_proof_pending(
db: Database, db: Database,
proof: Proof, proof: Proof,

View File

@@ -1,6 +1,6 @@
import asyncio import asyncio
import math import math
from typing import Dict, List, Optional, Set, Tuple from typing import Dict, List, Optional, Tuple
import bolt11 import bolt11
from loguru import logger from loguru import logger
@@ -49,7 +49,6 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerLightning):
crud: LedgerCrud, crud: LedgerCrud,
derivation_path="", derivation_path="",
): ):
self.secrets_used: Set[str] = set()
self.master_key = seed self.master_key = seed
self.derivation_path = derivation_path self.derivation_path = derivation_path
@@ -162,9 +161,10 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerLightning):
# Mark proofs as used and prepare new promises # Mark proofs as used and prepare new promises
secrets = set([p.secret for p in proofs]) secrets = set([p.secret for p in proofs])
self.secrets_used |= secrets self.secrets_used |= secrets
async with self.db.connect() as conn:
# store in db # store in db
for p in proofs: for p in proofs:
await self.crud.invalidate_proof(proof=p, db=self.db) await self.crud.invalidate_proof(proof=p, db=self.db, conn=conn)
async def _generate_change_promises( async def _generate_change_promises(
self, self,
@@ -538,70 +538,59 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerLightning):
async def _generate_promises( async def _generate_promises(
self, B_s: List[BlindedMessage], keyset: Optional[MintKeyset] = None self, B_s: List[BlindedMessage], keyset: Optional[MintKeyset] = None
) -> list[BlindedSignature]: ) -> list[BlindedSignature]:
"""Generates promises that sum to the given amount. """Generates a promises (Blind signatures) for given amount and returns a pair (amount, C').
Args: Args:
B_s (List[BlindedMessage]): _description_ B_s (List[BlindedMessage]): Blinded secret (point on curve)
keyset (Optional[MintKeyset], optional): _description_. Defaults to None.
Returns:
list[BlindedSignature]: _description_
"""
return [
await self._generate_promise(
b.amount, PublicKey(bytes.fromhex(b.B_), raw=True), keyset
)
for b in B_s
]
async def _generate_promise(
self, amount: int, B_: PublicKey, keyset: Optional[MintKeyset] = None
) -> BlindedSignature:
"""Generates a promise (Blind signature) for given amount and returns a pair (amount, C').
Args:
amount (int): Amount of the promise.
B_ (PublicKey): Blinded secret (point on curve)
keyset (Optional[MintKeyset], optional): Which keyset to use. Private keys will be taken from this keyset. Defaults to None. keyset (Optional[MintKeyset], optional): Which keyset to use. Private keys will be taken from this keyset. Defaults to None.
Returns: Returns:
BlindedSignature: Generated promise. list[BlindedSignature]: Generated BlindedSignatures.
""" """
keyset = keyset if keyset else self.keyset keyset = keyset if keyset else self.keyset
promises = []
for b in B_s:
amount = b.amount
B_ = PublicKey(bytes.fromhex(b.B_), raw=True)
logger.trace(f"Generating promise with keyset {keyset.id}.") logger.trace(f"Generating promise with keyset {keyset.id}.")
private_key_amount = keyset.private_keys[amount] private_key_amount = keyset.private_keys[amount]
C_, e, s = b_dhke.step2_bob(B_, private_key_amount) C_, e, s = b_dhke.step2_bob(B_, private_key_amount)
promises.append((B_, amount, C_, e, s))
signatures = []
async with self.db.connect() as conn:
for promise in promises:
B_, amount, C_, e, s = promise
logger.trace(f"crud: _generate_promise storing promise for {amount}") logger.trace(f"crud: _generate_promise storing promise for {amount}")
await self.crud.store_promise( await self.crud.store_promise(
amount=amount, amount=amount,
id=keyset.id,
B_=B_.serialize().hex(), B_=B_.serialize().hex(),
C_=C_.serialize().hex(), C_=C_.serialize().hex(),
e=e.serialize(), e=e.serialize(),
s=s.serialize(), s=s.serialize(),
db=self.db, db=self.db,
id=keyset.id, conn=conn,
) )
logger.trace(f"crud: _generate_promise stored promise for {amount}") logger.trace(f"crud: _generate_promise stored promise for {amount}")
return BlindedSignature( signature = BlindedSignature(
id=keyset.id, id=keyset.id,
amount=amount, amount=amount,
C_=C_.serialize().hex(), C_=C_.serialize().hex(),
dleq=DLEQ(e=e.serialize(), s=s.serialize()), dleq=DLEQ(e=e.serialize(), s=s.serialize()),
) )
signatures.append(signature)
return signatures
# ------- PROOFS ------- # ------- PROOFS -------
async def load_used_proofs(self) -> None: async def load_used_proofs(self) -> None:
"""Load all used proofs from database.""" """Load all used proofs from database."""
logger.trace("crud: loading used proofs") logger.debug("Loading used proofs into memory")
secrets_used = await self.crud.get_secrets_used(db=self.db) secrets_used = await self.crud.get_secrets_used(db=self.db)
logger.trace(f"crud: loaded {len(secrets_used)} used proofs") logger.debug(f"Loaded {len(secrets_used)} used proofs")
self.secrets_used = set(secrets_used) self.secrets_used = set(secrets_used)
def _check_spendable(self, proof: Proof) -> bool:
"""Checks whether the proof was already spent."""
return proof.secret not in self.secrets_used
async def _check_pending(self, proofs: List[Proof]) -> List[bool]: async def _check_pending(self, proofs: List[Proof]) -> List[bool]:
"""Checks whether the proof is still pending.""" """Checks whether the proof is still pending."""
proofs_pending = await self.crud.get_proofs_pending(db=self.db) proofs_pending = await self.crud.get_proofs_pending(db=self.db)
@@ -628,13 +617,12 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerLightning):
List[bool]: List of which proof is still spendable (True if still spendable, else False) List[bool]: List of which proof is still spendable (True if still spendable, else False)
List[bool]: List of which proof are pending (True if pending, else False) List[bool]: List of which proof are pending (True if pending, else False)
""" """
spendable = [self._check_spendable(p) for p in proofs]
spendable = await self._check_proofs_spendable(proofs)
pending = await self._check_pending(proofs) pending = await self._check_pending(proofs)
return spendable, pending return spendable, pending
async def _set_proofs_pending( async def _set_proofs_pending(self, proofs: List[Proof]) -> None:
self, proofs: List[Proof], conn: Optional[Connection] = None
) -> None:
"""If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to """If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to
the list of pending proofs or removes them. Used as a mutex for proofs. the list of pending proofs or removes them. Used as a mutex for proofs.
@@ -646,22 +634,24 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerLightning):
""" """
# first we check whether these proofs are pending already # first we check whether these proofs are pending already
async with self.proofs_pending_lock: async with self.proofs_pending_lock:
async with self.db.connect() as conn:
await self._validate_proofs_pending(proofs, conn) await self._validate_proofs_pending(proofs, conn)
for p in proofs: for p in proofs:
try: try:
await self.crud.set_proof_pending(proof=p, db=self.db, conn=conn) await self.crud.set_proof_pending(
proof=p, db=self.db, conn=conn
)
except Exception: except Exception:
raise TransactionError("proofs already pending.") raise TransactionError("proofs already pending.")
async def _unset_proofs_pending( async def _unset_proofs_pending(self, proofs: List[Proof]) -> None:
self, proofs: List[Proof], conn: Optional[Connection] = None
) -> None:
"""Deletes proofs from pending table. """Deletes proofs from pending table.
Args: Args:
proofs (List[Proof]): Proofs to delete. proofs (List[Proof]): Proofs to delete.
""" """
async with self.proofs_pending_lock: async with self.proofs_pending_lock:
async with self.db.connect() as conn:
for p in proofs: for p in proofs:
await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn) await self.crud.unset_proof_pending(proof=p, db=self.db, conn=conn)

View File

@@ -204,3 +204,13 @@ async def m009_add_out_to_invoices(db: Database):
await conn.execute( await conn.execute(
f"ALTER TABLE {table_with_schema(db, 'invoices')} ADD COLUMN out BOOL" f"ALTER TABLE {table_with_schema(db, 'invoices')} ADD COLUMN out BOOL"
) )
async def m010_add_index_to_proofs_used(db: Database):
# create index on proofs_used table for secret
async with db.connect() as conn:
await conn.execute(
"CREATE INDEX IF NOT EXISTS"
f" {table_with_schema(db, 'proofs_used')}_secret_idx ON"
f" {table_with_schema(db, 'proofs_used')} (secret)"
)

View File

@@ -47,6 +47,7 @@ async def rotate_keys(n_seconds=10):
async def start_mint_init(): async def start_mint_init():
await migrate_databases(ledger.db, migrations) await migrate_databases(ledger.db, migrations)
if settings.mint_cache_secrets:
await ledger.load_used_proofs() await ledger.load_used_proofs()
await ledger.init_keysets() await ledger.init_keysets()

View File

@@ -11,6 +11,7 @@ from ..core.base import (
) )
from ..core.crypto import b_dhke from ..core.crypto import b_dhke
from ..core.crypto.secp import PublicKey from ..core.crypto.secp import PublicKey
from ..core.db import Database
from ..core.errors import ( from ..core.errors import (
NoSecretInProofsError, NoSecretInProofsError,
NotAllowedError, NotAllowedError,
@@ -19,16 +20,19 @@ from ..core.errors import (
TransactionError, TransactionError,
) )
from ..core.settings import settings from ..core.settings import settings
from ..mint.crud import LedgerCrud
from .conditions import LedgerSpendingConditions from .conditions import LedgerSpendingConditions
from .protocols import SupportsKeysets from .protocols import SupportsDb, SupportsKeysets
class LedgerVerification(LedgerSpendingConditions, SupportsKeysets): class LedgerVerification(LedgerSpendingConditions, SupportsKeysets, SupportsDb):
"""Verification functions for the ledger.""" """Verification functions for the ledger."""
keyset: MintKeyset keyset: MintKeyset
keysets: MintKeysets keysets: MintKeysets
secrets_used: Set[str] secrets_used: Set[str] = set()
crud: LedgerCrud
db: Database
async def verify_inputs_and_outputs( async def verify_inputs_and_outputs(
self, proofs: List[Proof], outputs: Optional[List[BlindedMessage]] = None self, proofs: List[Proof], outputs: Optional[List[BlindedMessage]] = None
@@ -48,7 +52,9 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):
""" """
# Verify inputs # Verify inputs
# Verify proofs are spendable # Verify proofs are spendable
self._check_proofs_spendable(proofs) spendable = await self._check_proofs_spendable(proofs)
if not all(spendable):
raise TokenAlreadySpentError()
# Verify amounts of inputs # Verify amounts of inputs
if not all([self._verify_amount(p.amount) for p in proofs]): if not all([self._verify_amount(p.amount) for p in proofs]):
raise TransactionError("invalid amount.") raise TransactionError("invalid amount.")
@@ -87,10 +93,24 @@ class LedgerVerification(LedgerSpendingConditions, SupportsKeysets):
if not self._verify_no_duplicate_outputs(outputs): if not self._verify_no_duplicate_outputs(outputs):
raise TransactionError("duplicate outputs.") raise TransactionError("duplicate outputs.")
def _check_proofs_spendable(self, proofs: List[Proof]): async def _check_proofs_spendable(self, proofs: List[Proof]) -> List[bool]:
"""Checks whether the proofs were already spent.""" """Checks whether the proof was already spent."""
if not all([p.secret not in self.secrets_used for p in proofs]): spendable_states = []
raise TokenAlreadySpentError() if settings.mint_cache_secrets:
# check used secrets in memory
for p in proofs:
spendable_state = p.secret not in self.secrets_used
spendable_states.append(spendable_state)
else:
# check used secrets in database
async with self.db.connect() as conn:
for p in proofs:
spendable_state = (
await self.crud.get_proof_used(db=self.db, proof=p, conn=conn)
is None
)
spendable_states.append(spendable_state)
return spendable_states
def _verify_secret_criteria(self, proof: Proof) -> Literal[True]: def _verify_secret_criteria(self, proof: Proof) -> Literal[True]:
"""Verifies that a secret is present and is not too long (DOS prevention).""" """Verifies that a secret is present and is not too long (DOS prevention)."""

View File

@@ -94,6 +94,7 @@ def async_set_httpx_client(func):
proxies=proxies_dict, # type: ignore proxies=proxies_dict, # type: ignore
headers=headers_dict, headers=headers_dict,
base_url=self.url, base_url=self.url,
timeout=None if settings.debug else 5,
) )
return await func(self, *args, **kwargs) return await func(self, *args, **kwargs)

173
poetry.lock generated
View File

@@ -1,10 +1,9 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]] [[package]]
name = "anyio" name = "anyio"
version = "4.0.0" version = "4.0.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations" description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -26,7 +25,6 @@ trio = ["trio (>=0.22)"]
name = "asn1crypto" name = "asn1crypto"
version = "1.5.1" 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" 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 optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
@@ -38,7 +36,6 @@ files = [
name = "attrs" name = "attrs"
version = "23.1.0" version = "23.1.0"
description = "Classes Without Boilerplate" description = "Classes Without Boilerplate"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -57,7 +54,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte
name = "base58" name = "base58"
version = "2.1.1" version = "2.1.1"
description = "Base58 and Base58Check implementation." description = "Base58 and Base58Check implementation."
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -72,7 +68,6 @@ tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "
name = "bech32" name = "bech32"
version = "1.2.0" version = "1.2.0"
description = "Reference implementation for Bech32 and segwit addresses." description = "Reference implementation for Bech32 and segwit addresses."
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -84,7 +79,6 @@ files = [
name = "bip32" name = "bip32"
version = "3.4" version = "3.4"
description = "Minimalistic implementation of the BIP32 key derivation scheme" description = "Minimalistic implementation of the BIP32 key derivation scheme"
category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
@@ -100,7 +94,6 @@ coincurve = ">=15.0,<19"
name = "bitstring" name = "bitstring"
version = "3.1.9" version = "3.1.9"
description = "Simple construction, analysis and modification of binary data." description = "Simple construction, analysis and modification of binary data."
category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
@@ -113,7 +106,6 @@ files = [
name = "black" name = "black"
version = "23.9.1" version = "23.9.1"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -160,7 +152,6 @@ uvloop = ["uvloop (>=0.15.2)"]
name = "bolt11" name = "bolt11"
version = "2.0.5" version = "2.0.5"
description = "A library for encoding and decoding BOLT11 payment requests." description = "A library for encoding and decoding BOLT11 payment requests."
category = "main"
optional = false optional = false
python-versions = ">=3.8.1" python-versions = ">=3.8.1"
files = [ files = [
@@ -180,7 +171,6 @@ secp256k1 = "*"
name = "certifi" name = "certifi"
version = "2023.7.22" version = "2023.7.22"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -192,7 +182,6 @@ files = [
name = "cffi" name = "cffi"
version = "1.16.0" version = "1.16.0"
description = "Foreign Function Interface for Python calling C code." description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -257,7 +246,6 @@ pycparser = "*"
name = "cfgv" name = "cfgv"
version = "3.4.0" version = "3.4.0"
description = "Validate configuration and produce human readable error messages." description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -269,7 +257,6 @@ files = [
name = "click" name = "click"
version = "8.1.7" version = "8.1.7"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -284,7 +271,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "coincurve" name = "coincurve"
version = "18.0.0" version = "18.0.0"
description = "Cross-platform Python CFFI bindings for libsecp256k1" description = "Cross-platform Python CFFI bindings for libsecp256k1"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -340,7 +326,6 @@ cffi = ">=1.3.0"
name = "colorama" name = "colorama"
version = "0.4.6" version = "0.4.6"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
category = "main"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [ files = [
@@ -352,7 +337,6 @@ files = [
name = "coverage" name = "coverage"
version = "7.3.2" version = "7.3.2"
description = "Code coverage measurement for Python" description = "Code coverage measurement for Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -420,7 +404,6 @@ toml = ["tomli"]
name = "cryptography" name = "cryptography"
version = "41.0.4" version = "41.0.4"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -466,7 +449,6 @@ test-randomorder = ["pytest-randomly"]
name = "distlib" name = "distlib"
version = "0.3.7" version = "0.3.7"
description = "Distribution utilities" description = "Distribution utilities"
category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
@@ -478,7 +460,6 @@ files = [
name = "ecdsa" name = "ecdsa"
version = "0.18.0" version = "0.18.0"
description = "ECDSA cryptographic signature library (pure python)" description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [ files = [
@@ -497,7 +478,6 @@ gmpy2 = ["gmpy2"]
name = "environs" name = "environs"
version = "9.5.0" version = "9.5.0"
description = "simplified environment variable parsing" description = "simplified environment variable parsing"
category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -519,7 +499,6 @@ tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
name = "exceptiongroup" name = "exceptiongroup"
version = "1.1.3" version = "1.1.3"
description = "Backport of PEP 654 (exception groups)" description = "Backport of PEP 654 (exception groups)"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -534,7 +513,6 @@ test = ["pytest (>=6)"]
name = "fastapi" name = "fastapi"
version = "0.103.0" version = "0.103.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -550,11 +528,24 @@ typing-extensions = ">=4.5.0"
[package.extras] [package.extras]
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "fastapi-profiler"
version = "1.2.0"
description = "A FastAPI Middleware of pyinstrument to check your service performance."
optional = false
python-versions = "*"
files = [
{file = "fastapi_profiler-1.2.0-py3-none-any.whl", hash = "sha256:71615f815c5ff4fe193c14b1ecf6bfc250502c6adfca64a219340df5eb0a7a9b"},
]
[package.dependencies]
fastapi = "*"
pyinstrument = ">=4.4.0"
[[package]] [[package]]
name = "filelock" name = "filelock"
version = "3.12.4" version = "3.12.4"
description = "A platform independent file lock." description = "A platform independent file lock."
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -571,7 +562,6 @@ typing = ["typing-extensions (>=4.7.1)"]
name = "h11" name = "h11"
version = "0.14.0" version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -583,7 +573,6 @@ files = [
name = "httpcore" name = "httpcore"
version = "0.18.0" version = "0.18.0"
description = "A minimal low-level HTTP client." description = "A minimal low-level HTTP client."
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -595,17 +584,16 @@ files = [
anyio = ">=3.0,<5.0" anyio = ">=3.0,<5.0"
certifi = "*" certifi = "*"
h11 = ">=0.13,<0.15" h11 = ">=0.13,<0.15"
sniffio = ">=1.0.0,<2.0.0" sniffio = "==1.*"
[package.extras] [package.extras]
http2 = ["h2 (>=3,<5)"] http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"] socks = ["socksio (==1.*)"]
[[package]] [[package]]
name = "httpx" name = "httpx"
version = "0.25.1" version = "0.25.1"
description = "The next generation HTTP client." description = "The next generation HTTP client."
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -619,19 +607,18 @@ certifi = "*"
httpcore = "*" httpcore = "*"
idna = "*" idna = "*"
sniffio = "*" sniffio = "*"
socksio = {version = ">=1.0.0,<2.0.0", optional = true, markers = "extra == \"socks\""} socksio = {version = "==1.*", optional = true, markers = "extra == \"socks\""}
[package.extras] [package.extras]
brotli = ["brotli", "brotlicffi"] brotli = ["brotli", "brotlicffi"]
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"] http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"] socks = ["socksio (==1.*)"]
[[package]] [[package]]
name = "identify" name = "identify"
version = "2.5.30" version = "2.5.30"
description = "File identification library for Python" description = "File identification library for Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -646,7 +633,6 @@ license = ["ukkonen"]
name = "idna" name = "idna"
version = "3.4" version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)" description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -658,7 +644,6 @@ files = [
name = "importlib-metadata" name = "importlib-metadata"
version = "6.8.0" version = "6.8.0"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -678,7 +663,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs
name = "iniconfig" name = "iniconfig"
version = "2.0.0" version = "2.0.0"
description = "brain-dead simple config-ini parsing" description = "brain-dead simple config-ini parsing"
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -690,7 +674,6 @@ files = [
name = "loguru" name = "loguru"
version = "0.7.2" version = "0.7.2"
description = "Python logging made (stupidly) simple" description = "Python logging made (stupidly) simple"
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -709,7 +692,6 @@ dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptio
name = "marshmallow" name = "marshmallow"
version = "3.20.1" version = "3.20.1"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes." description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -730,7 +712,6 @@ tests = ["pytest", "pytz", "simplejson"]
name = "mnemonic" name = "mnemonic"
version = "0.20" version = "0.20"
description = "Implementation of Bitcoin BIP-0039" description = "Implementation of Bitcoin BIP-0039"
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -742,7 +723,6 @@ files = [
name = "mypy" name = "mypy"
version = "1.6.0" version = "1.6.0"
description = "Optional static typing for Python" description = "Optional static typing for Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -789,7 +769,6 @@ reports = ["lxml"]
name = "mypy-extensions" name = "mypy-extensions"
version = "1.0.0" version = "1.0.0"
description = "Type system extensions for programs checked with the mypy type checker." description = "Type system extensions for programs checked with the mypy type checker."
category = "dev"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -801,7 +780,6 @@ files = [
name = "nodeenv" name = "nodeenv"
version = "1.8.0" version = "1.8.0"
description = "Node.js virtual environment builder" description = "Node.js virtual environment builder"
category = "dev"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
files = [ files = [
@@ -816,7 +794,6 @@ setuptools = "*"
name = "outcome" name = "outcome"
version = "1.2.0" version = "1.2.0"
description = "Capture the outcome of Python function calls." description = "Capture the outcome of Python function calls."
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -831,7 +808,6 @@ attrs = ">=19.2.0"
name = "packaging" name = "packaging"
version = "23.2" version = "23.2"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -843,7 +819,6 @@ files = [
name = "pathspec" name = "pathspec"
version = "0.11.2" version = "0.11.2"
description = "Utility library for gitignore style pattern matching of file paths." description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -855,7 +830,6 @@ files = [
name = "platformdirs" name = "platformdirs"
version = "3.11.0" version = "3.11.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -871,7 +845,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co
name = "pluggy" name = "pluggy"
version = "1.3.0" version = "1.3.0"
description = "plugin and hook calling mechanisms for python" description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -887,7 +860,6 @@ testing = ["pytest", "pytest-benchmark"]
name = "pre-commit" name = "pre-commit"
version = "3.5.0" version = "3.5.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -906,7 +878,6 @@ virtualenv = ">=20.10.0"
name = "psycopg2-binary" name = "psycopg2-binary"
version = "2.9.9" version = "2.9.9"
description = "psycopg2 - Python-PostgreSQL Database Adapter" description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = true optional = true
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -985,7 +956,6 @@ files = [
name = "pycparser" name = "pycparser"
version = "2.21" version = "2.21"
description = "C parser in Python" description = "C parser in Python"
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [ files = [
@@ -997,7 +967,6 @@ files = [
name = "pycryptodomex" name = "pycryptodomex"
version = "3.19.0" version = "3.19.0"
description = "Cryptographic library for Python" description = "Cryptographic library for Python"
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [ files = [
@@ -1039,7 +1008,6 @@ files = [
name = "pydantic" name = "pydantic"
version = "1.10.13" version = "1.10.13"
description = "Data validation and settings management using python type hints" description = "Data validation and settings management using python type hints"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1088,11 +1056,86 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"] dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"] email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pyinstrument"
version = "4.6.1"
description = "Call stack profiler for Python. Shows you why your code is slow!"
optional = false
python-versions = ">=3.7"
files = [
{file = "pyinstrument-4.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:73476e4bc6e467ac1b2c3c0dd1f0b71c9061d4de14626676adfdfbb14aa342b4"},
{file = "pyinstrument-4.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4d1da8efd974cf9df52ee03edaee2d3875105ddd00de35aa542760f7c612bdf7"},
{file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:507be1ee2f2b0c9fba74d622a272640dd6d1b0c9ec3388b2cdeb97ad1e77125f"},
{file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cee6de08eb45754ef4f602ce52b640d1c535d934a6a8733a974daa095def37"},
{file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7873e8cec92321251fdf894a72b3c78f4c5c20afdd1fef0baf9042ec843bb04"},
{file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a242f6cac40bc83e1f3002b6b53681846dfba007f366971db0bf21e02dbb1903"},
{file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97c9660cdb4bd2a43cf4f3ab52cffd22f3ac9a748d913b750178fb34e5e39e64"},
{file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e304cd0723e2b18ada5e63c187abf6d777949454c734f5974d64a0865859f0f4"},
{file = "pyinstrument-4.6.1-cp310-cp310-win32.whl", hash = "sha256:cee21a2d78187dd8a80f72f5d0f1ddb767b2d9800f8bb4d94b6d11f217c22cdb"},
{file = "pyinstrument-4.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:2000712f71d693fed2f8a1c1638d37b7919124f367b37976d07128d49f1445eb"},
{file = "pyinstrument-4.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a366c6f3dfb11f1739bdc1dee75a01c1563ad0bf4047071e5e77598087df457f"},
{file = "pyinstrument-4.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6be327be65d934796558aa9cb0f75ce62ebd207d49ad1854610c97b0579ad47"},
{file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e160d9c5d20d3e4ef82269e4e8b246ff09bdf37af5fb8cb8ccca97936d95ad6"},
{file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ffbf56605ef21c2fcb60de2fa74ff81f417d8be0c5002a407e414d6ef6dee43"},
{file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92cc4924596d6e8f30a16182bbe90893b1572d847ae12652f72b34a9a17c24a"},
{file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f4b48a94d938cae981f6948d9ec603bab2087b178d2095d042d5a48aabaecaab"},
{file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7a386392275bdef4a1849712dc5b74f0023483fca14ef93d0ca27d453548982"},
{file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:871b131b83e9b1122f2325061c68ed1e861eebcb568c934d2fb193652f077f77"},
{file = "pyinstrument-4.6.1-cp311-cp311-win32.whl", hash = "sha256:8d8515156dd91f5652d13b5fcc87e634f8fe1c07b68d1d0840348cdd50bf5ace"},
{file = "pyinstrument-4.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb868fbe089036e9f32525a249f4c78b8dc46967612393f204b8234f439c9cc4"},
{file = "pyinstrument-4.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a18cd234cce4f230f1733807f17a134e64a1f1acabf74a14d27f583cf2b183df"},
{file = "pyinstrument-4.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:574cfca69150be4ce4461fb224712fbc0722a49b0dc02fa204d02807adf6b5a0"},
{file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e02cf505e932eb8ccf561b7527550a67ec14fcae1fe0e25319b09c9c166e914"},
{file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832fb2acef9d53701c1ab546564c45fb70a8770c816374f8dd11420d399103c9"},
{file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13cb57e9607545623ebe462345b3d0c4caee0125d2d02267043ece8aca8f4ea0"},
{file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9be89e7419bcfe8dd6abb0d959d6d9c439c613a4a873514c43d16b48dae697c9"},
{file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:476785cfbc44e8e1b1ad447398aa3deae81a8df4d37eb2d8bbb0c404eff979cd"},
{file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e9cebd90128a3d2fee36d3ccb665c1b9dce75261061b2046203e45c4a8012d54"},
{file = "pyinstrument-4.6.1-cp312-cp312-win32.whl", hash = "sha256:1d0b76683df2ad5c40eff73607dc5c13828c92fbca36aff1ddf869a3c5a55fa6"},
{file = "pyinstrument-4.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:c4b7af1d9d6a523cfbfedebcb69202242d5bd0cb89c4e094cc73d5d6e38279bd"},
{file = "pyinstrument-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79ae152f8c6a680a188fb3be5e0f360ac05db5bbf410169a6c40851dfaebcce9"},
{file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cad2745964c174c65aa75f1bf68a4394d1b4d28f33894837cfd315d1e836f0"},
{file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb81f66f7f94045d723069cf317453d42375de9ff3c69089cf6466b078ac1db4"},
{file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab30ae75969da99e9a529e21ff497c18fdf958e822753db4ae7ed1e67094040"},
{file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f36cb5b644762fb3c86289324bbef17e95f91cd710603ac19444a47f638e8e96"},
{file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8b45075d9dbbc977dbc7007fb22bb0054c6990fbe91bf48dd80c0b96c6307ba7"},
{file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:475ac31477f6302e092463896d6a2055f3e6abcd293bad16ff94fc9185308a88"},
{file = "pyinstrument-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:29172ab3d8609fdf821c3f2562dc61e14f1a8ff5306607c32ca743582d3a760e"},
{file = "pyinstrument-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:bd176f297c99035127b264369d2bb97a65255f65f8d4e843836baf55ebb3cee4"},
{file = "pyinstrument-4.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:23e9b4526978432e9999021da9a545992cf2ac3df5ee82db7beb6908fc4c978c"},
{file = "pyinstrument-4.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2dbcaccc9f456ef95557ec501caeb292119c24446d768cb4fb43578b0f3d572c"},
{file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2097f63c66c2bc9678c826b9ff0c25acde3ed455590d9dcac21220673fe74fbf"},
{file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:205ac2e76bd65d61b9611a9ce03d5f6393e34ec5b41dd38808f25d54e6b3e067"},
{file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f414ddf1161976a40fc0a333000e6a4ad612719eac0b8c9bb73f47153187148"},
{file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65e62ebfa2cd8fb57eda90006f4505ac4c70da00fc2f05b6d8337d776ea76d41"},
{file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d96309df4df10be7b4885797c5f69bb3a89414680ebaec0722d8156fde5268c3"},
{file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f3d1ad3bc8ebb4db925afa706aa865c4bfb40d52509f143491ac0df2440ee5d2"},
{file = "pyinstrument-4.6.1-cp38-cp38-win32.whl", hash = "sha256:dc37cb988c8854eb42bda2e438aaf553536566657d157c4473cc8aad5692a779"},
{file = "pyinstrument-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:2cd4ce750c34a0318fc2d6c727cc255e9658d12a5cf3f2d0473f1c27157bdaeb"},
{file = "pyinstrument-4.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ca95b21f022e995e062b371d1f42d901452bcbedd2c02f036de677119503355"},
{file = "pyinstrument-4.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ac1e1d7e1f1b64054c4eb04eb4869a7a5eef2261440e73943cc1b1bc3c828c18"},
{file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0711845e953fce6ab781221aacffa2a66dbc3289f8343e5babd7b2ea34da6c90"},
{file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7d28582017de35cb64eb4e4fa603e753095108ca03745f5d17295970ee631f"},
{file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7be57db08bd366a37db3aa3a6187941ee21196e8b14975db337ddc7d1490649d"},
{file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9a0ac0f56860398d2628ce389826ce83fb3a557d0c9a2351e8a2eac6eb869983"},
{file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a9045186ff13bc826fef16be53736a85029aae3c6adfe52e666cad00d7ca623b"},
{file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6c4c56b6eab9004e92ad8a48bb54913fdd71fc8a748ae42a27b9e26041646f8b"},
{file = "pyinstrument-4.6.1-cp39-cp39-win32.whl", hash = "sha256:37e989c44b51839d0c97466fa2b623638b9470d56d79e329f359f0e8fa6d83db"},
{file = "pyinstrument-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:5494c5a84fee4309d7d973366ca6b8b9f8ba1d6b254e93b7c506264ef74f2cef"},
{file = "pyinstrument-4.6.1.tar.gz", hash = "sha256:f4731b27121350f5a983d358d2272fe3df2f538aed058f57217eef7801a89288"},
]
[package.extras]
bin = ["click", "nox"]
docs = ["furo (==2021.6.18b36)", "myst-parser (==0.15.1)", "sphinx (==4.2.0)", "sphinxcontrib-programoutput (==0.17)"]
examples = ["django", "numpy"]
test = ["flaky", "greenlet (>=3.0.0a1)", "ipython", "pytest", "pytest-asyncio (==0.12.0)", "sphinx-autobuild (==2021.3.14)", "trio"]
types = ["typing-extensions"]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "7.4.2" version = "7.4.2"
description = "pytest: simple powerful testing with Python" description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1115,7 +1158,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
name = "pytest-asyncio" name = "pytest-asyncio"
version = "0.21.1" version = "0.21.1"
description = "Pytest support for asyncio" description = "Pytest support for asyncio"
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1134,7 +1176,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy
name = "pytest-cov" name = "pytest-cov"
version = "4.1.0" version = "4.1.0"
description = "Pytest plugin for measuring coverage." description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1153,7 +1194,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
name = "python-dotenv" name = "python-dotenv"
version = "1.0.0" version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables" description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1168,7 +1208,6 @@ cli = ["click (>=5.0)"]
name = "pyyaml" name = "pyyaml"
version = "6.0.1" version = "6.0.1"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -1218,7 +1257,6 @@ files = [
name = "represent" name = "represent"
version = "1.6.0.post0" version = "1.6.0.post0"
description = "Create __repr__ automatically or declaratively." description = "Create __repr__ automatically or declaratively."
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [ files = [
@@ -1236,7 +1274,6 @@ test = ["ipython", "mock", "pytest (>=3.0.5)"]
name = "ruff" name = "ruff"
version = "0.0.284" version = "0.0.284"
description = "An extremely fast Python linter, written in Rust." description = "An extremely fast Python linter, written in Rust."
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1263,7 +1300,6 @@ files = [
name = "secp256k1" name = "secp256k1"
version = "0.14.0" version = "0.14.0"
description = "FFI bindings to libsecp256k1" description = "FFI bindings to libsecp256k1"
category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
@@ -1299,7 +1335,6 @@ cffi = ">=1.3.0"
name = "setuptools" name = "setuptools"
version = "68.2.2" version = "68.2.2"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1316,7 +1351,6 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
description = "Python 2 and 3 compatibility utilities" description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [ files = [
@@ -1328,7 +1362,6 @@ files = [
name = "sniffio" name = "sniffio"
version = "1.3.0" version = "1.3.0"
description = "Sniff out which async library your code is running under" description = "Sniff out which async library your code is running under"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1340,7 +1373,6 @@ files = [
name = "socksio" name = "socksio"
version = "1.0.0" version = "1.0.0"
description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5."
category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -1352,7 +1384,6 @@ files = [
name = "sqlalchemy" name = "sqlalchemy"
version = "1.3.24" version = "1.3.24"
description = "Database Abstraction Library" description = "Database Abstraction Library"
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [ files = [
@@ -1408,7 +1439,6 @@ pymysql = ["pymysql", "pymysql (<1)"]
name = "sqlalchemy-aio" name = "sqlalchemy-aio"
version = "0.17.0" version = "0.17.0"
description = "Async support for SQLAlchemy." description = "Async support for SQLAlchemy."
category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -1430,7 +1460,6 @@ trio = ["trio (>=0.15)"]
name = "starlette" name = "starlette"
version = "0.27.0" version = "0.27.0"
description = "The little ASGI library that shines." description = "The little ASGI library that shines."
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1449,7 +1478,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
name = "tomli" name = "tomli"
version = "2.0.1" version = "2.0.1"
description = "A lil' TOML parser" description = "A lil' TOML parser"
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1461,7 +1489,6 @@ files = [
name = "typing-extensions" name = "typing-extensions"
version = "4.8.0" version = "4.8.0"
description = "Backported and Experimental Type Hints for Python 3.8+" description = "Backported and Experimental Type Hints for Python 3.8+"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1473,7 +1500,6 @@ files = [
name = "uvicorn" name = "uvicorn"
version = "0.23.2" version = "0.23.2"
description = "The lightning-fast ASGI server." description = "The lightning-fast ASGI server."
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1493,7 +1519,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
name = "virtualenv" name = "virtualenv"
version = "20.24.5" version = "20.24.5"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1514,7 +1539,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
name = "websocket-client" name = "websocket-client"
version = "1.6.4" version = "1.6.4"
description = "WebSocket client for Python with low level API options" description = "WebSocket client for Python with low level API options"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1531,7 +1555,6 @@ test = ["websockets"]
name = "wheel" name = "wheel"
version = "0.41.2" version = "0.41.2"
description = "A built-package format for Python" description = "A built-package format for Python"
category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1546,7 +1569,6 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"]
name = "win32-setctime" name = "win32-setctime"
version = "1.1.0" version = "1.1.0"
description = "A small Python utility to set file creation time on Windows" description = "A small Python utility to set file creation time on Windows"
category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
@@ -1561,7 +1583,6 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
name = "zipp" name = "zipp"
version = "3.17.0" version = "3.17.0"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
@@ -1579,4 +1600,4 @@ pgsql = ["psycopg2-binary"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.8.1" python-versions = "^3.8.1"
content-hash = "fd3495f7158b86ad25037517a1dfe033cfd367d75857b4db5c9db10f233bb842" content-hash = "b2c312fd906aa18a26712039f700322c2c20889a95e1cd9af787df54d700b2ca"

View File

@@ -43,6 +43,7 @@ pytest-cov = "^4.0.0"
pytest = "^7.4.0" pytest = "^7.4.0"
ruff = "^0.0.284" ruff = "^0.0.284"
pre-commit = "^3.3.3" pre-commit = "^3.3.3"
fastapi-profiler = "^1.2.0"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]

View File

@@ -20,6 +20,7 @@ from cashu.mint.ledger import Ledger
SERVER_PORT = 3337 SERVER_PORT = 3337
SERVER_ENDPOINT = f"http://localhost:{SERVER_PORT}" SERVER_ENDPOINT = f"http://localhost:{SERVER_PORT}"
settings.debug = True
settings.cashu_dir = "./test_data/" settings.cashu_dir = "./test_data/"
settings.mint_host = "localhost" settings.mint_host = "localhost"
settings.mint_port = SERVER_PORT settings.mint_port = SERVER_PORT
@@ -55,6 +56,7 @@ class UvicornServer(multiprocessing.Process):
async def ledger(): async def ledger():
async def start_mint_init(ledger: Ledger): async def start_mint_init(ledger: Ledger):
await migrate_databases(ledger.db, migrations_mint) await migrate_databases(ledger.db, migrations_mint)
if settings.mint_cache_secrets:
await ledger.load_used_proofs() await ledger.load_used_proofs()
await ledger.init_keysets() await ledger.init_keysets()