From 35c5542b2f09dc4dbce6ba8dd3e1889b275a39d9 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:24:13 +0100 Subject: [PATCH] restore 7f64f426c226977530bc983650efd5dbcd4afa84 --- .env.example | 5 + .github/codecov.yml | 9 + lnbits/commands.py | 16 +- lnbits/extensions/cashu/README.md | 11 + lnbits/extensions/cashu/__init__.py | 48 + lnbits/extensions/cashu/config.json | 7 + lnbits/extensions/cashu/crud.py | 63 + lnbits/extensions/cashu/migrations.py | 33 + lnbits/extensions/cashu/models.py | 147 ++ lnbits/extensions/cashu/static/js/base64.js | 37 + lnbits/extensions/cashu/static/js/dhke.js | 39 + .../cashu/static/js/noble-secp256k1.js | 1178 +++++++++ lnbits/extensions/cashu/static/js/utils.js | 23 + lnbits/extensions/cashu/tasks.py | 33 + .../cashu/templates/cashu/_api_docs.html | 80 + .../cashu/templates/cashu/_cashu.html | 13 + .../cashu/templates/cashu/index.html | 367 +++ .../cashu/templates/cashu/mint.html | 76 + .../cashu/templates/cashu/wallet.html | 2337 +++++++++++++++++ lnbits/extensions/cashu/views.py | 224 ++ lnbits/extensions/cashu/views_api.py | 382 +++ lnbits/helpers.py | 4 + poetry.lock | 744 +++--- pyproject.toml | 42 +- requirements.txt | 58 +- tests/data/mock_data.zip | Bin 30790 -> 37478 bytes tools/conv.py | 6 +- 27 files changed, 5624 insertions(+), 358 deletions(-) create mode 100644 .github/codecov.yml create mode 100644 lnbits/extensions/cashu/README.md create mode 100644 lnbits/extensions/cashu/__init__.py create mode 100644 lnbits/extensions/cashu/config.json create mode 100644 lnbits/extensions/cashu/crud.py create mode 100644 lnbits/extensions/cashu/migrations.py create mode 100644 lnbits/extensions/cashu/models.py create mode 100644 lnbits/extensions/cashu/static/js/base64.js create mode 100644 lnbits/extensions/cashu/static/js/dhke.js create mode 100644 lnbits/extensions/cashu/static/js/noble-secp256k1.js create mode 100644 lnbits/extensions/cashu/static/js/utils.js create mode 100644 lnbits/extensions/cashu/tasks.py create mode 100644 lnbits/extensions/cashu/templates/cashu/_api_docs.html create mode 100644 lnbits/extensions/cashu/templates/cashu/_cashu.html create mode 100644 lnbits/extensions/cashu/templates/cashu/index.html create mode 100644 lnbits/extensions/cashu/templates/cashu/mint.html create mode 100644 lnbits/extensions/cashu/templates/cashu/wallet.html create mode 100644 lnbits/extensions/cashu/views.py create mode 100644 lnbits/extensions/cashu/views_api.py diff --git a/.env.example b/.env.example index b7fb9f06..898f90bd 100644 --- a/.env.example +++ b/.env.example @@ -103,3 +103,8 @@ ECLAIR_PASS=eclairpw # Enter /api in LightningTipBot to get your key LNTIPS_API_KEY=LNTIPS_ADMIN_KEY LNTIPS_API_ENDPOINT=https://ln.tips + +# Cashu Mint +# Use a long-enough random (!) private key. +# Once set, you cannot change this key as for now. +CASHU_PRIVATE_KEY="SuperSecretPrivateKey" diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 00000000..e190e6aa --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,9 @@ +coverage: + status: + patch: off + project: + default: + target: auto + # adjust accordingly based on how flaky your tests are + # this allows a 10% drop from the previous base commit coverage + threshold: 10% \ No newline at end of file diff --git a/lnbits/commands.py b/lnbits/commands.py index 0f7454f2..89739076 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -65,14 +65,14 @@ async def migrate_databases(): (db_name, version, version), ) - async def run_migration(db, migrations_module): - db_name = migrations_module.__name__.split(".")[-2] + async def run_migration(db, migrations_module, db_name): for key, migrate in migrations_module.__dict__.items(): match = match = matcher.match(key) if match: version = int(match.group(1)) if version > current_versions.get(db_name, 0): logger.debug(f"running migration {db_name}.{version}") + logger.debug(f"db = {db}") await migrate(db) if db.schema == None: @@ -97,20 +97,24 @@ async def migrate_databases(): rows = await (await conn.execute("SELECT * FROM dbversions")).fetchall() current_versions = {row["db"]: row["version"] for row in rows} matcher = re.compile(r"^m(\d\d\d)_") - await run_migration(conn, core_migrations) + db_name = core_migrations.__name__.split(".")[-2] + await run_migration(conn, core_migrations, db_name) for ext in get_valid_extensions(): try: - ext_migrations = importlib.import_module( - f"lnbits.extensions.{ext.code}.migrations" + + module_str = ( + ext.migration_module or f"lnbits.extensions.{ext.code}.migrations" ) + ext_migrations = importlib.import_module(module_str) ext_db = importlib.import_module(f"lnbits.extensions.{ext.code}").db + db_name = ext.db_name or module_str.split(".")[-2] except ImportError: raise ImportError( f"Please make sure that the extension `{ext.code}` has a migrations file." ) async with ext_db.connect() as ext_conn: - await run_migration(ext_conn, ext_migrations) + await run_migration(ext_conn, ext_migrations, db_name) logger.info("✔️ All migrations done.") diff --git a/lnbits/extensions/cashu/README.md b/lnbits/extensions/cashu/README.md new file mode 100644 index 00000000..8f53b474 --- /dev/null +++ b/lnbits/extensions/cashu/README.md @@ -0,0 +1,11 @@ +# Cashu + +## Create ecash mint for pegging in/out of ecash + + + +### Usage + +1. Enable extension +2. Create a Mint +3. Share wallet diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py new file mode 100644 index 00000000..e6507bba --- /dev/null +++ b/lnbits/extensions/cashu/__init__.py @@ -0,0 +1,48 @@ +import asyncio + +from environs import Env # type: ignore +from fastapi import APIRouter +from fastapi.staticfiles import StaticFiles + +from lnbits.db import Database +from lnbits.helpers import template_renderer +from lnbits.tasks import catch_everything_and_restart + +db = Database("ext_cashu") + +import sys + +cashu_static_files = [ + { + "path": "/cashu/static", + "app": StaticFiles(directory="lnbits/extensions/cashu/static"), + "name": "cashu_static", + } +] +from cashu.mint.ledger import Ledger + +env = Env() +env.read_env() + +ledger = Ledger( + db=db, + seed=env.str("CASHU_PRIVATE_KEY", default="SuperSecretPrivateKey"), + derivation_path="0/0/0/1", +) + +cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"]) + + +def cashu_renderer(): + return template_renderer(["lnbits/extensions/cashu/templates"]) + + +from .tasks import startup_cashu_mint, wait_for_paid_invoices +from .views import * # noqa +from .views_api import * # noqa + + +def cashu_start(): + loop = asyncio.get_event_loop() + loop.create_task(catch_everything_and_restart(startup_cashu_mint)) + loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json new file mode 100644 index 00000000..af202d43 --- /dev/null +++ b/lnbits/extensions/cashu/config.json @@ -0,0 +1,7 @@ +{ + "name": "Cashu", + "short_description": "Ecash mint and wallet", + "icon": "account_balance", + "contributors": ["calle", "vlad", "arcbtc"], + "hidden": false +} diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py new file mode 100644 index 00000000..773a11fd --- /dev/null +++ b/lnbits/extensions/cashu/crud.py @@ -0,0 +1,63 @@ +import os +import random +import time +from binascii import hexlify, unhexlify +from typing import Any, List, Optional, Union + +from cashu.core.base import MintKeyset +from embit import bip32, bip39, ec, script +from embit.networks import NETWORKS +from loguru import logger + +from lnbits.db import Connection, Database +from lnbits.helpers import urlsafe_short_hash + +from . import db +from .models import Cashu, Pegs, Promises, Proof + + +async def create_cashu( + cashu_id: str, keyset_id: str, wallet_id: str, data: Cashu +) -> Cashu: + + await db.execute( + """ + INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, keyset_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + cashu_id, + wallet_id, + data.name, + data.tickershort, + data.fraction, + data.maxsats, + data.coins, + keyset_id, + ), + ) + + cashu = await get_cashu(cashu_id) + assert cashu, "Newly created cashu couldn't be retrieved" + return cashu + + +async def get_cashu(cashu_id) -> Optional[Cashu]: + row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,)) + return Cashu(**row) if row else None + + +async def get_cashus(wallet_ids: Union[str, List[str]]) -> List[Cashu]: + if isinstance(wallet_ids, str): + wallet_ids = [wallet_ids] + + q = ",".join(["?"] * len(wallet_ids)) + rows = await db.fetchall( + f"SELECT * FROM cashu.cashu WHERE wallet IN ({q})", (*wallet_ids,) + ) + + return [Cashu(**row) for row in rows] + + +async def delete_cashu(cashu_id) -> None: + await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,)) diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py new file mode 100644 index 00000000..b54c4108 --- /dev/null +++ b/lnbits/extensions/cashu/migrations.py @@ -0,0 +1,33 @@ +async def m001_initial(db): + """ + Initial cashu table. + """ + await db.execute( + """ + CREATE TABLE cashu.cashu ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + name TEXT NOT NULL, + tickershort TEXT DEFAULT 'sats', + fraction BOOL, + maxsats INT, + coins INT, + keyset_id TEXT NOT NULL, + issued_sat INT + ); + """ + ) + + """ + Initial cashus table. + """ + await db.execute( + """ + CREATE TABLE cashu.pegs ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + inout BOOL NOT NULL, + amount INT + ); + """ + ) diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py new file mode 100644 index 00000000..c820d12e --- /dev/null +++ b/lnbits/extensions/cashu/models.py @@ -0,0 +1,147 @@ +from sqlite3 import Row +from typing import List, Union + +from fastapi import Query +from pydantic import BaseModel + + +class Cashu(BaseModel): + id: str = Query(None) + name: str = Query(None) + wallet: str = Query(None) + tickershort: str = Query(None) + fraction: bool = Query(None) + maxsats: int = Query(0) + coins: int = Query(0) + keyset_id: str = Query(None) + + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + + +class Pegs(BaseModel): + id: str + wallet: str + inout: str + amount: str + + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + + +class PayLnurlWData(BaseModel): + lnurl: str + + +class Promises(BaseModel): + id: str + amount: int + B_b: str + C_b: str + cashu_id: str + + +class Proof(BaseModel): + amount: int + secret: str + C: str + reserved: bool = False # whether this proof is reserved for sending + send_id: str = "" # unique ID of send attempt + time_created: str = "" + time_reserved: str = "" + + @classmethod + def from_row(cls, row: Row): + return cls( + amount=row[0], + C=row[1], + secret=row[2], + reserved=row[3] or False, + send_id=row[4] or "", + time_created=row[5] or "", + time_reserved=row[6] or "", + ) + + @classmethod + def from_dict(cls, d: dict): + assert "secret" in d, "no secret in proof" + assert "amount" in d, "no amount in proof" + return cls( + amount=d.get("amount"), + C=d.get("C"), + secret=d.get("secret"), + reserved=d.get("reserved") or False, + send_id=d.get("send_id") or "", + time_created=d.get("time_created") or "", + time_reserved=d.get("time_reserved") or "", + ) + + def to_dict(self): + return dict(amount=self.amount, secret=self.secret, C=self.C) + + def __getitem__(self, key): + return self.__getattribute__(key) + + def __setitem__(self, key, val): + self.__setattr__(key, val) + + +class Proofs(BaseModel): + """TODO: Use this model""" + + proofs: List[Proof] + + +class Invoice(BaseModel): + amount: int + pr: str + hash: str + issued: bool = False + + @classmethod + def from_row(cls, row: Row): + return cls( + amount=int(row[0]), + pr=str(row[1]), + hash=str(row[2]), + issued=bool(row[3]), + ) + + +class BlindedMessage(BaseModel): + amount: int + B_: str + + +class BlindedSignature(BaseModel): + amount: int + C_: str + + @classmethod + def from_dict(cls, d: dict): + return cls( + amount=d["amount"], + C_=d["C_"], + ) + + +class MintPayloads(BaseModel): + blinded_messages: List[BlindedMessage] = [] + + +class SplitPayload(BaseModel): + proofs: List[Proof] + amount: int + output_data: MintPayloads + + +class CheckPayload(BaseModel): + proofs: List[Proof] + + +class MeltPayload(BaseModel): + proofs: List[Proof] + amount: int + invoice: str diff --git a/lnbits/extensions/cashu/static/js/base64.js b/lnbits/extensions/cashu/static/js/base64.js new file mode 100644 index 00000000..b150882f --- /dev/null +++ b/lnbits/extensions/cashu/static/js/base64.js @@ -0,0 +1,37 @@ +function unescapeBase64Url(str) { + return (str + '==='.slice((str.length + 3) % 4)) + .replace(/-/g, '+') + .replace(/_/g, '/') +} + +function escapeBase64Url(str) { + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') +} + +const uint8ToBase64 = (function (exports) { + 'use strict' + + var fromCharCode = String.fromCharCode + var encode = function encode(uint8array) { + var output = [] + + for (var i = 0, length = uint8array.length; i < length; i++) { + output.push(fromCharCode(uint8array[i])) + } + + return btoa(output.join('')) + } + + var asCharCode = function asCharCode(c) { + return c.charCodeAt(0) + } + + var decode = function decode(chars) { + return Uint8Array.from(atob(chars), asCharCode) + } + + exports.decode = decode + exports.encode = encode + + return exports +})({}) diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js new file mode 100644 index 00000000..41c2fb46 --- /dev/null +++ b/lnbits/extensions/cashu/static/js/dhke.js @@ -0,0 +1,39 @@ +async function hashToCurve(secretMessage) { + console.log( + '### secretMessage', + nobleSecp256k1.utils.bytesToHex(secretMessage) + ) + let point + while (!point) { + const hash = await nobleSecp256k1.utils.sha256(secretMessage) + const hashHex = nobleSecp256k1.utils.bytesToHex(hash) + const pointX = '02' + hashHex + console.log('### pointX', pointX) + try { + point = nobleSecp256k1.Point.fromHex(pointX) + console.log('### point', point.toHex()) + } catch (error) { + secretMessage = await nobleSecp256k1.utils.sha256(secretMessage) + } + } + return point +} + +async function step1Alice(secretMessage) { + // todo: document & validate `secretMessage` format + secretMessage = uint8ToBase64.encode(secretMessage) + secretMessage = new TextEncoder().encode(secretMessage) + const Y = await hashToCurve(secretMessage) + const rpk = nobleSecp256k1.utils.randomPrivateKey() + const r = bytesToNumber(rpk) + const P = nobleSecp256k1.Point.fromPrivateKey(r) + const B_ = Y.add(P) + return {B_: B_.toHex(true), r: nobleSecp256k1.utils.bytesToHex(rpk)} +} + +function step3Alice(C_, r, A) { + // const rInt = BigInt(r) + const rInt = bytesToNumber(r) + const C = C_.subtract(A.multiply(rInt)) + return C +} diff --git a/lnbits/extensions/cashu/static/js/noble-secp256k1.js b/lnbits/extensions/cashu/static/js/noble-secp256k1.js new file mode 100644 index 00000000..6a6bd441 --- /dev/null +++ b/lnbits/extensions/cashu/static/js/noble-secp256k1.js @@ -0,0 +1,1178 @@ +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? factory(exports) + : typeof define === 'function' && define.amd + ? define(['exports'], factory) + : ((global = + typeof globalThis !== 'undefined' ? globalThis : global || self), + factory((global.nobleSecp256k1 = {}))) +})(this, function (exports) { + 'use strict' + + const _nodeResolve_empty = {} + + const nodeCrypto = /*#__PURE__*/ Object.freeze({ + __proto__: null, + default: _nodeResolve_empty + }) + + /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ + const _0n = BigInt(0) + const _1n = BigInt(1) + const _2n = BigInt(2) + const _3n = BigInt(3) + const _8n = BigInt(8) + const POW_2_256 = _2n ** BigInt(256) + const CURVE = { + a: _0n, + b: BigInt(7), + P: POW_2_256 - _2n ** BigInt(32) - BigInt(977), + n: POW_2_256 - BigInt('432420386565659656852420866394968145599'), + h: _1n, + Gx: BigInt( + '55066263022277343669578718895168534326250603453777594175500187360389116729240' + ), + Gy: BigInt( + '32670510020758816978083085130507043184471273380659243275938904335757337482424' + ), + beta: BigInt( + '0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee' + ) + } + function weistrass(x) { + const {a, b} = CURVE + const x2 = mod(x * x) + const x3 = mod(x2 * x) + return mod(x3 + a * x + b) + } + const USE_ENDOMORPHISM = CURVE.a === _0n + class JacobianPoint { + constructor(x, y, z) { + this.x = x + this.y = y + this.z = z + } + static fromAffine(p) { + if (!(p instanceof Point)) { + throw new TypeError('JacobianPoint#fromAffine: expected Point') + } + return new JacobianPoint(p.x, p.y, _1n) + } + static toAffineBatch(points) { + const toInv = invertBatch(points.map(p => p.z)) + return points.map((p, i) => p.toAffine(toInv[i])) + } + static normalizeZ(points) { + return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine) + } + equals(other) { + if (!(other instanceof JacobianPoint)) + throw new TypeError('JacobianPoint expected') + const {x: X1, y: Y1, z: Z1} = this + const {x: X2, y: Y2, z: Z2} = other + const Z1Z1 = mod(Z1 ** _2n) + const Z2Z2 = mod(Z2 ** _2n) + const U1 = mod(X1 * Z2Z2) + const U2 = mod(X2 * Z1Z1) + const S1 = mod(mod(Y1 * Z2) * Z2Z2) + const S2 = mod(mod(Y2 * Z1) * Z1Z1) + return U1 === U2 && S1 === S2 + } + negate() { + return new JacobianPoint(this.x, mod(-this.y), this.z) + } + double() { + const {x: X1, y: Y1, z: Z1} = this + const A = mod(X1 ** _2n) + const B = mod(Y1 ** _2n) + const C = mod(B ** _2n) + const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C)) + const E = mod(_3n * A) + const F = mod(E ** _2n) + const X3 = mod(F - _2n * D) + const Y3 = mod(E * (D - X3) - _8n * C) + const Z3 = mod(_2n * Y1 * Z1) + return new JacobianPoint(X3, Y3, Z3) + } + add(other) { + if (!(other instanceof JacobianPoint)) + throw new TypeError('JacobianPoint expected') + const {x: X1, y: Y1, z: Z1} = this + const {x: X2, y: Y2, z: Z2} = other + if (X2 === _0n || Y2 === _0n) return this + if (X1 === _0n || Y1 === _0n) return other + const Z1Z1 = mod(Z1 ** _2n) + const Z2Z2 = mod(Z2 ** _2n) + const U1 = mod(X1 * Z2Z2) + const U2 = mod(X2 * Z1Z1) + const S1 = mod(mod(Y1 * Z2) * Z2Z2) + const S2 = mod(mod(Y2 * Z1) * Z1Z1) + const H = mod(U2 - U1) + const r = mod(S2 - S1) + if (H === _0n) { + if (r === _0n) { + return this.double() + } else { + return JacobianPoint.ZERO + } + } + const HH = mod(H ** _2n) + const HHH = mod(H * HH) + const V = mod(U1 * HH) + const X3 = mod(r ** _2n - HHH - _2n * V) + const Y3 = mod(r * (V - X3) - S1 * HHH) + const Z3 = mod(Z1 * Z2 * H) + return new JacobianPoint(X3, Y3, Z3) + } + subtract(other) { + return this.add(other.negate()) + } + multiplyUnsafe(scalar) { + const P0 = JacobianPoint.ZERO + if (typeof scalar === 'bigint' && scalar === _0n) return P0 + let n = normalizeScalar(scalar) + if (n === _1n) return this + if (!USE_ENDOMORPHISM) { + let p = P0 + let d = this + while (n > _0n) { + if (n & _1n) p = p.add(d) + d = d.double() + n >>= _1n + } + return p + } + let {k1neg, k1, k2neg, k2} = splitScalarEndo(n) + let k1p = P0 + let k2p = P0 + let d = this + while (k1 > _0n || k2 > _0n) { + if (k1 & _1n) k1p = k1p.add(d) + if (k2 & _1n) k2p = k2p.add(d) + d = d.double() + k1 >>= _1n + k2 >>= _1n + } + if (k1neg) k1p = k1p.negate() + if (k2neg) k2p = k2p.negate() + k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z) + return k1p.add(k2p) + } + precomputeWindow(W) { + const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1 + const points = [] + let p = this + let base = p + for (let window = 0; window < windows; window++) { + base = p + points.push(base) + for (let i = 1; i < 2 ** (W - 1); i++) { + base = base.add(p) + points.push(base) + } + p = base.double() + } + return points + } + wNAF(n, affinePoint) { + if (!affinePoint && this.equals(JacobianPoint.BASE)) + affinePoint = Point.BASE + const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1 + if (256 % W) { + throw new Error( + 'Point#wNAF: Invalid precomputation window, must be power of 2' + ) + } + let precomputes = affinePoint && pointPrecomputes.get(affinePoint) + if (!precomputes) { + precomputes = this.precomputeWindow(W) + if (affinePoint && W !== 1) { + precomputes = JacobianPoint.normalizeZ(precomputes) + pointPrecomputes.set(affinePoint, precomputes) + } + } + let p = JacobianPoint.ZERO + let f = JacobianPoint.ZERO + const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W) + const windowSize = 2 ** (W - 1) + const mask = BigInt(2 ** W - 1) + const maxNumber = 2 ** W + const shiftBy = BigInt(W) + for (let window = 0; window < windows; window++) { + const offset = window * windowSize + let wbits = Number(n & mask) + n >>= shiftBy + if (wbits > windowSize) { + wbits -= maxNumber + n += _1n + } + if (wbits === 0) { + let pr = precomputes[offset] + if (window % 2) pr = pr.negate() + f = f.add(pr) + } else { + let cached = precomputes[offset + Math.abs(wbits) - 1] + if (wbits < 0) cached = cached.negate() + p = p.add(cached) + } + } + return {p, f} + } + multiply(scalar, affinePoint) { + let n = normalizeScalar(scalar) + let point + let fake + if (USE_ENDOMORPHISM) { + const {k1neg, k1, k2neg, k2} = splitScalarEndo(n) + let {p: k1p, f: f1p} = this.wNAF(k1, affinePoint) + let {p: k2p, f: f2p} = this.wNAF(k2, affinePoint) + if (k1neg) k1p = k1p.negate() + if (k2neg) k2p = k2p.negate() + k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z) + point = k1p.add(k2p) + fake = f1p.add(f2p) + } else { + const {p, f} = this.wNAF(n, affinePoint) + point = p + fake = f + } + return JacobianPoint.normalizeZ([point, fake])[0] + } + toAffine(invZ = invert(this.z)) { + const {x, y, z} = this + const iz1 = invZ + const iz2 = mod(iz1 * iz1) + const iz3 = mod(iz2 * iz1) + const ax = mod(x * iz2) + const ay = mod(y * iz3) + const zz = mod(z * iz1) + if (zz !== _1n) throw new Error('invZ was invalid') + return new Point(ax, ay) + } + } + JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n) + JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n) + const pointPrecomputes = new WeakMap() + class Point { + constructor(x, y) { + this.x = x + this.y = y + } + _setWindowSize(windowSize) { + this._WINDOW_SIZE = windowSize + pointPrecomputes.delete(this) + } + static fromCompressedHex(bytes) { + const isShort = bytes.length === 32 + const x = bytesToNumber(isShort ? bytes : bytes.subarray(1)) + if (!isValidFieldElement(x)) throw new Error('Point is not on curve') + const y2 = weistrass(x) + let y = sqrtMod(y2) + const isYOdd = (y & _1n) === _1n + if (isShort) { + if (isYOdd) y = mod(-y) + } else { + const isFirstByteOdd = (bytes[0] & 1) === 1 + if (isFirstByteOdd !== isYOdd) y = mod(-y) + } + const point = new Point(x, y) + point.assertValidity() + return point + } + static fromUncompressedHex(bytes) { + const x = bytesToNumber(bytes.subarray(1, 33)) + const y = bytesToNumber(bytes.subarray(33, 65)) + const point = new Point(x, y) + point.assertValidity() + return point + } + static fromHex(hex) { + const bytes = ensureBytes(hex) + const len = bytes.length + const header = bytes[0] + if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { + return this.fromCompressedHex(bytes) + } + if (len === 65 && header === 0x04) return this.fromUncompressedHex(bytes) + throw new Error( + `Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}` + ) + } + static fromPrivateKey(privateKey) { + return Point.BASE.multiply(normalizePrivateKey(privateKey)) + } + static fromSignature(msgHash, signature, recovery) { + msgHash = ensureBytes(msgHash) + const h = truncateHash(msgHash) + const {r, s} = normalizeSignature(signature) + if (recovery !== 0 && recovery !== 1) { + throw new Error('Cannot recover signature: invalid recovery bit') + } + const prefix = recovery & 1 ? '03' : '02' + const R = Point.fromHex(prefix + numTo32bStr(r)) + const {n} = CURVE + const rinv = invert(r, n) + const u1 = mod(-h * rinv, n) + const u2 = mod(s * rinv, n) + const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2) + if (!Q) throw new Error('Cannot recover signature: point at infinify') + Q.assertValidity() + return Q + } + toRawBytes(isCompressed = false) { + return hexToBytes(this.toHex(isCompressed)) + } + toHex(isCompressed = false) { + const x = numTo32bStr(this.x) + if (isCompressed) { + const prefix = this.y & _1n ? '03' : '02' + return `${prefix}${x}` + } else { + return `04${x}${numTo32bStr(this.y)}` + } + } + toHexX() { + return this.toHex(true).slice(2) + } + toRawX() { + return this.toRawBytes(true).slice(1) + } + assertValidity() { + const msg = 'Point is not on elliptic curve' + const {x, y} = this + if (!isValidFieldElement(x) || !isValidFieldElement(y)) + throw new Error(msg) + const left = mod(y * y) + const right = weistrass(x) + if (mod(left - right) !== _0n) throw new Error(msg) + } + equals(other) { + return this.x === other.x && this.y === other.y + } + negate() { + return new Point(this.x, mod(-this.y)) + } + double() { + return JacobianPoint.fromAffine(this).double().toAffine() + } + add(other) { + return JacobianPoint.fromAffine(this) + .add(JacobianPoint.fromAffine(other)) + .toAffine() + } + subtract(other) { + return this.add(other.negate()) + } + multiply(scalar) { + return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine() + } + multiplyAndAddUnsafe(Q, a, b) { + const P = JacobianPoint.fromAffine(this) + const aP = + a === _0n || a === _1n || this !== Point.BASE + ? P.multiplyUnsafe(a) + : P.multiply(a) + const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b) + const sum = aP.add(bQ) + return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine() + } + } + Point.BASE = new Point(CURVE.Gx, CURVE.Gy) + Point.ZERO = new Point(_0n, _0n) + function sliceDER(s) { + return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s + } + function parseDERInt(data) { + if (data.length < 2 || data[0] !== 0x02) { + throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`) + } + const len = data[1] + const res = data.subarray(2, len + 2) + if (!len || res.length !== len) { + throw new Error(`Invalid signature integer: wrong length`) + } + if (res[0] === 0x00 && res[1] <= 0x7f) { + throw new Error('Invalid signature integer: trailing length') + } + return {data: bytesToNumber(res), left: data.subarray(len + 2)} + } + function parseDERSignature(data) { + if (data.length < 2 || data[0] != 0x30) { + throw new Error(`Invalid signature tag: ${bytesToHex(data)}`) + } + if (data[1] !== data.length - 2) { + throw new Error('Invalid signature: incorrect length') + } + const {data: r, left: sBytes} = parseDERInt(data.subarray(2)) + const {data: s, left: rBytesLeft} = parseDERInt(sBytes) + if (rBytesLeft.length) { + throw new Error( + `Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}` + ) + } + return {r, s} + } + class Signature { + constructor(r, s) { + this.r = r + this.s = s + this.assertValidity() + } + static fromCompact(hex) { + const arr = isUint8a(hex) + const name = 'Signature.fromCompact' + if (typeof hex !== 'string' && !arr) + throw new TypeError(`${name}: Expected string or Uint8Array`) + const str = arr ? bytesToHex(hex) : hex + if (str.length !== 128) throw new Error(`${name}: Expected 64-byte hex`) + return new Signature( + hexToNumber(str.slice(0, 64)), + hexToNumber(str.slice(64, 128)) + ) + } + static fromDER(hex) { + const arr = isUint8a(hex) + if (typeof hex !== 'string' && !arr) + throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`) + const {r, s} = parseDERSignature(arr ? hex : hexToBytes(hex)) + return new Signature(r, s) + } + static fromHex(hex) { + return this.fromDER(hex) + } + assertValidity() { + const {r, s} = this + if (!isWithinCurveOrder(r)) + throw new Error('Invalid Signature: r must be 0 < r < n') + if (!isWithinCurveOrder(s)) + throw new Error('Invalid Signature: s must be 0 < s < n') + } + hasHighS() { + const HALF = CURVE.n >> _1n + return this.s > HALF + } + normalizeS() { + return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this + } + toDERRawBytes(isCompressed = false) { + return hexToBytes(this.toDERHex(isCompressed)) + } + toDERHex(isCompressed = false) { + const sHex = sliceDER(numberToHexUnpadded(this.s)) + if (isCompressed) return sHex + const rHex = sliceDER(numberToHexUnpadded(this.r)) + const rLen = numberToHexUnpadded(rHex.length / 2) + const sLen = numberToHexUnpadded(sHex.length / 2) + const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4) + return `30${length}02${rLen}${rHex}02${sLen}${sHex}` + } + toRawBytes() { + return this.toDERRawBytes() + } + toHex() { + return this.toDERHex() + } + toCompactRawBytes() { + return hexToBytes(this.toCompactHex()) + } + toCompactHex() { + return numTo32bStr(this.r) + numTo32bStr(this.s) + } + } + function concatBytes(...arrays) { + if (!arrays.every(isUint8a)) throw new Error('Uint8Array list expected') + if (arrays.length === 1) return arrays[0] + const length = arrays.reduce((a, arr) => a + arr.length, 0) + const result = new Uint8Array(length) + for (let i = 0, pad = 0; i < arrays.length; i++) { + const arr = arrays[i] + result.set(arr, pad) + pad += arr.length + } + return result + } + function isUint8a(bytes) { + return bytes instanceof Uint8Array + } + const hexes = Array.from({length: 256}, (v, i) => + i.toString(16).padStart(2, '0') + ) + function bytesToHex(uint8a) { + if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array') + let hex = '' + for (let i = 0; i < uint8a.length; i++) { + hex += hexes[uint8a[i]] + } + return hex + } + function numTo32bStr(num) { + if (num > POW_2_256) throw new Error('Expected number < 2^256') + return num.toString(16).padStart(64, '0') + } + function numTo32b(num) { + return hexToBytes(numTo32bStr(num)) + } + function numberToHexUnpadded(num) { + const hex = num.toString(16) + return hex.length & 1 ? `0${hex}` : hex + } + function hexToNumber(hex) { + if (typeof hex !== 'string') { + throw new TypeError('hexToNumber: expected string, got ' + typeof hex) + } + return BigInt(`0x${hex}`) + } + function hexToBytes(hex) { + if (typeof hex !== 'string') { + throw new TypeError('hexToBytes: expected string, got ' + typeof hex) + } + if (hex.length % 2) + throw new Error('hexToBytes: received invalid unpadded hex' + hex.length) + const array = new Uint8Array(hex.length / 2) + for (let i = 0; i < array.length; i++) { + const j = i * 2 + const hexByte = hex.slice(j, j + 2) + const byte = Number.parseInt(hexByte, 16) + if (Number.isNaN(byte) || byte < 0) + throw new Error('Invalid byte sequence') + array[i] = byte + } + return array + } + function bytesToNumber(bytes) { + return hexToNumber(bytesToHex(bytes)) + } + function ensureBytes(hex) { + return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex) + } + function normalizeScalar(num) { + if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) + return BigInt(num) + if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num + throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n') + } + function mod(a, b = CURVE.P) { + const result = a % b + return result >= _0n ? result : b + result + } + function pow2(x, power) { + const {P} = CURVE + let res = x + while (power-- > _0n) { + res *= res + res %= P + } + return res + } + function sqrtMod(x) { + const {P} = CURVE + const _6n = BigInt(6) + const _11n = BigInt(11) + const _22n = BigInt(22) + const _23n = BigInt(23) + const _44n = BigInt(44) + const _88n = BigInt(88) + const b2 = (x * x * x) % P + const b3 = (b2 * b2 * x) % P + const b6 = (pow2(b3, _3n) * b3) % P + const b9 = (pow2(b6, _3n) * b3) % P + const b11 = (pow2(b9, _2n) * b2) % P + const b22 = (pow2(b11, _11n) * b11) % P + const b44 = (pow2(b22, _22n) * b22) % P + const b88 = (pow2(b44, _44n) * b44) % P + const b176 = (pow2(b88, _88n) * b88) % P + const b220 = (pow2(b176, _44n) * b44) % P + const b223 = (pow2(b220, _3n) * b3) % P + const t1 = (pow2(b223, _23n) * b22) % P + const t2 = (pow2(t1, _6n) * b2) % P + return pow2(t2, _2n) + } + function invert(number, modulo = CURVE.P) { + if (number === _0n || modulo <= _0n) { + throw new Error( + `invert: expected positive integers, got n=${number} mod=${modulo}` + ) + } + let a = mod(number, modulo) + let b = modulo + let x = _0n, + u = _1n + while (a !== _0n) { + const q = b / a + const r = b % a + const m = x - u * q + ;(b = a), (a = r), (x = u), (u = m) + } + const gcd = b + if (gcd !== _1n) throw new Error('invert: does not exist') + return mod(x, modulo) + } + function invertBatch(nums, p = CURVE.P) { + const scratch = new Array(nums.length) + const lastMultiplied = nums.reduce((acc, num, i) => { + if (num === _0n) return acc + scratch[i] = acc + return mod(acc * num, p) + }, _1n) + const inverted = invert(lastMultiplied, p) + nums.reduceRight((acc, num, i) => { + if (num === _0n) return acc + scratch[i] = mod(acc * scratch[i], p) + return mod(acc * num, p) + }, inverted) + return scratch + } + const divNearest = (a, b) => (a + b / _2n) / b + const POW_2_128 = _2n ** BigInt(128) + function splitScalarEndo(k) { + const {n} = CURVE + const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15') + const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3') + const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8') + const b2 = a1 + const c1 = divNearest(b2 * k, n) + const c2 = divNearest(-b1 * k, n) + let k1 = mod(k - c1 * a1 - c2 * a2, n) + let k2 = mod(-c1 * b1 - c2 * b2, n) + const k1neg = k1 > POW_2_128 + const k2neg = k2 > POW_2_128 + if (k1neg) k1 = n - k1 + if (k2neg) k2 = n - k2 + if (k1 > POW_2_128 || k2 > POW_2_128) { + throw new Error('splitScalarEndo: Endomorphism failed, k=' + k) + } + return {k1neg, k1, k2neg, k2} + } + function truncateHash(hash) { + const {n} = CURVE + const byteLength = hash.length + const delta = byteLength * 8 - 256 + let h = bytesToNumber(hash) + if (delta > 0) h = h >> BigInt(delta) + if (h >= n) h -= n + return h + } + class HmacDrbg { + constructor() { + this.v = new Uint8Array(32).fill(1) + this.k = new Uint8Array(32).fill(0) + this.counter = 0 + } + hmac(...values) { + return utils.hmacSha256(this.k, ...values) + } + hmacSync(...values) { + if (typeof utils.hmacSha256Sync !== 'function') + throw new Error('utils.hmacSha256Sync is undefined, you need to set it') + const res = utils.hmacSha256Sync(this.k, ...values) + if (res instanceof Promise) + throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync') + return res + } + incr() { + if (this.counter >= 1000) { + throw new Error('Tried 1,000 k values for sign(), all were invalid') + } + this.counter += 1 + } + async reseed(seed = new Uint8Array()) { + this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed) + this.v = await this.hmac(this.v) + if (seed.length === 0) return + this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed) + this.v = await this.hmac(this.v) + } + reseedSync(seed = new Uint8Array()) { + this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed) + this.v = this.hmacSync(this.v) + if (seed.length === 0) return + this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed) + this.v = this.hmacSync(this.v) + } + async generate() { + this.incr() + this.v = await this.hmac(this.v) + return this.v + } + generateSync() { + this.incr() + this.v = this.hmacSync(this.v) + return this.v + } + } + function isWithinCurveOrder(num) { + return _0n < num && num < CURVE.n + } + function isValidFieldElement(num) { + return _0n < num && num < CURVE.P + } + function kmdToSig(kBytes, m, d) { + const k = bytesToNumber(kBytes) + if (!isWithinCurveOrder(k)) return + const {n} = CURVE + const q = Point.BASE.multiply(k) + const r = mod(q.x, n) + if (r === _0n) return + const s = mod(invert(k, n) * mod(m + d * r, n), n) + if (s === _0n) return + const sig = new Signature(r, s) + const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n) + return {sig, recovery} + } + function normalizePrivateKey(key) { + let num + if (typeof key === 'bigint') { + num = key + } else if ( + typeof key === 'number' && + Number.isSafeInteger(key) && + key > 0 + ) { + num = BigInt(key) + } else if (typeof key === 'string') { + if (key.length !== 64) throw new Error('Expected 32 bytes of private key') + num = hexToNumber(key) + } else if (isUint8a(key)) { + if (key.length !== 32) throw new Error('Expected 32 bytes of private key') + num = bytesToNumber(key) + } else { + throw new TypeError('Expected valid private key') + } + if (!isWithinCurveOrder(num)) + throw new Error('Expected private key: 0 < key < n') + return num + } + function normalizePublicKey(publicKey) { + if (publicKey instanceof Point) { + publicKey.assertValidity() + return publicKey + } else { + return Point.fromHex(publicKey) + } + } + function normalizeSignature(signature) { + if (signature instanceof Signature) { + signature.assertValidity() + return signature + } + try { + return Signature.fromDER(signature) + } catch (error) { + return Signature.fromCompact(signature) + } + } + function getPublicKey(privateKey, isCompressed = false) { + return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed) + } + function recoverPublicKey( + msgHash, + signature, + recovery, + isCompressed = false + ) { + return Point.fromSignature(msgHash, signature, recovery).toRawBytes( + isCompressed + ) + } + function isPub(item) { + const arr = isUint8a(item) + const str = typeof item === 'string' + const len = (arr || str) && item.length + if (arr) return len === 33 || len === 65 + if (str) return len === 66 || len === 130 + if (item instanceof Point) return true + return false + } + function getSharedSecret(privateA, publicB, isCompressed = false) { + if (isPub(privateA)) + throw new TypeError('getSharedSecret: first arg must be private key') + if (!isPub(publicB)) + throw new TypeError('getSharedSecret: second arg must be public key') + const b = normalizePublicKey(publicB) + b.assertValidity() + return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed) + } + function bits2int(bytes) { + const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes + return bytesToNumber(slice) + } + function bits2octets(bytes) { + const z1 = bits2int(bytes) + const z2 = mod(z1, CURVE.n) + return int2octets(z2 < _0n ? z1 : z2) + } + function int2octets(num) { + if (typeof num !== 'bigint') throw new Error('Expected bigint') + const hex = numTo32bStr(num) + return hexToBytes(hex) + } + function initSigArgs(msgHash, privateKey, extraEntropy) { + if (msgHash == null) + throw new Error(`sign: expected valid message hash, not "${msgHash}"`) + const h1 = ensureBytes(msgHash) + const d = normalizePrivateKey(privateKey) + const seedArgs = [int2octets(d), bits2octets(h1)] + if (extraEntropy != null) { + if (extraEntropy === true) extraEntropy = utils.randomBytes(32) + const e = ensureBytes(extraEntropy) + if (e.length !== 32) + throw new Error('sign: Expected 32 bytes of extra data') + seedArgs.push(e) + } + const seed = concatBytes(...seedArgs) + const m = bits2int(h1) + return {seed, m, d} + } + function finalizeSig(recSig, opts) { + let {sig, recovery} = recSig + const {canonical, der, recovered} = Object.assign( + {canonical: true, der: true}, + opts + ) + if (canonical && sig.hasHighS()) { + sig = sig.normalizeS() + recovery ^= 1 + } + const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes() + return recovered ? [hashed, recovery] : hashed + } + async function sign(msgHash, privKey, opts = {}) { + const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy) + let sig + const drbg = new HmacDrbg() + await drbg.reseed(seed) + while (!(sig = kmdToSig(await drbg.generate(), m, d))) await drbg.reseed() + return finalizeSig(sig, opts) + } + function signSync(msgHash, privKey, opts = {}) { + const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy) + let sig + const drbg = new HmacDrbg() + drbg.reseedSync(seed) + while (!(sig = kmdToSig(drbg.generateSync(), m, d))) drbg.reseedSync() + return finalizeSig(sig, opts) + } + const vopts = {strict: true} + function verify(signature, msgHash, publicKey, opts = vopts) { + let sig + try { + sig = normalizeSignature(signature) + msgHash = ensureBytes(msgHash) + } catch (error) { + return false + } + const {r, s} = sig + if (opts.strict && sig.hasHighS()) return false + const h = truncateHash(msgHash) + let P + try { + P = normalizePublicKey(publicKey) + } catch (error) { + return false + } + const {n} = CURVE + const sinv = invert(s, n) + const u1 = mod(h * sinv, n) + const u2 = mod(r * sinv, n) + const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2) + if (!R) return false + const v = mod(R.x, n) + return v === r + } + function finalizeSchnorrChallenge(ch) { + return mod(bytesToNumber(ch), CURVE.n) + } + function hasEvenY(point) { + return (point.y & _1n) === _0n + } + class SchnorrSignature { + constructor(r, s) { + this.r = r + this.s = s + this.assertValidity() + } + static fromHex(hex) { + const bytes = ensureBytes(hex) + if (bytes.length !== 64) + throw new TypeError( + `SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}` + ) + const r = bytesToNumber(bytes.subarray(0, 32)) + const s = bytesToNumber(bytes.subarray(32, 64)) + return new SchnorrSignature(r, s) + } + assertValidity() { + const {r, s} = this + if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) + throw new Error('Invalid signature') + } + toHex() { + return numTo32bStr(this.r) + numTo32bStr(this.s) + } + toRawBytes() { + return hexToBytes(this.toHex()) + } + } + function schnorrGetPublicKey(privateKey) { + return Point.fromPrivateKey(privateKey).toRawX() + } + function initSchnorrSigArgs(message, privateKey, auxRand) { + if (message == null) + throw new TypeError(`sign: Expected valid message, not "${message}"`) + const m = ensureBytes(message) + const d0 = normalizePrivateKey(privateKey) + const rand = ensureBytes(auxRand) + if (rand.length !== 32) + throw new TypeError('sign: Expected 32 bytes of aux randomness') + const P = Point.fromPrivateKey(d0) + const px = P.toRawX() + const d = hasEvenY(P) ? d0 : CURVE.n - d0 + return {m, P, px, d, rand} + } + function initSchnorrNonce(d, t0h) { + return numTo32b(d ^ bytesToNumber(t0h)) + } + function finalizeSchnorrNonce(k0h) { + const k0 = mod(bytesToNumber(k0h), CURVE.n) + if (k0 === _0n) + throw new Error('sign: Creation of signature failed. k is zero') + const R = Point.fromPrivateKey(k0) + const rx = R.toRawX() + const k = hasEvenY(R) ? k0 : CURVE.n - k0 + return {R, rx, k} + } + function finalizeSchnorrSig(R, k, e, d) { + return new SchnorrSignature(R.x, mod(k + e * d, CURVE.n)).toRawBytes() + } + async function schnorrSign( + message, + privateKey, + auxRand = utils.randomBytes() + ) { + const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand) + const t = initSchnorrNonce(d, await utils.taggedHash(TAGS.aux, rand)) + const {R, rx, k} = finalizeSchnorrNonce( + await utils.taggedHash(TAGS.nonce, t, px, m) + ) + const e = finalizeSchnorrChallenge( + await utils.taggedHash(TAGS.challenge, rx, px, m) + ) + const sig = finalizeSchnorrSig(R, k, e, d) + const isValid = await schnorrVerify(sig, m, px) + if (!isValid) throw new Error('sign: Invalid signature produced') + return sig + } + function schnorrSignSync(message, privateKey, auxRand = utils.randomBytes()) { + const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand) + const t = initSchnorrNonce(d, utils.taggedHashSync(TAGS.aux, rand)) + const {R, rx, k} = finalizeSchnorrNonce( + utils.taggedHashSync(TAGS.nonce, t, px, m) + ) + const e = finalizeSchnorrChallenge( + utils.taggedHashSync(TAGS.challenge, rx, px, m) + ) + const sig = finalizeSchnorrSig(R, k, e, d) + const isValid = schnorrVerifySync(sig, m, px) + if (!isValid) throw new Error('sign: Invalid signature produced') + return sig + } + function initSchnorrVerify(signature, message, publicKey) { + const raw = signature instanceof SchnorrSignature + const sig = raw ? signature : SchnorrSignature.fromHex(signature) + if (raw) sig.assertValidity() + return { + ...sig, + m: ensureBytes(message), + P: normalizePublicKey(publicKey) + } + } + function finalizeSchnorrVerify(r, P, s, e) { + const R = Point.BASE.multiplyAndAddUnsafe( + P, + normalizePrivateKey(s), + mod(-e, CURVE.n) + ) + if (!R || !hasEvenY(R) || R.x !== r) return false + return true + } + async function schnorrVerify(signature, message, publicKey) { + try { + const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey) + const e = finalizeSchnorrChallenge( + await utils.taggedHash(TAGS.challenge, numTo32b(r), P.toRawX(), m) + ) + return finalizeSchnorrVerify(r, P, s, e) + } catch (error) { + return false + } + } + function schnorrVerifySync(signature, message, publicKey) { + try { + const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey) + const e = finalizeSchnorrChallenge( + utils.taggedHashSync(TAGS.challenge, numTo32b(r), P.toRawX(), m) + ) + return finalizeSchnorrVerify(r, P, s, e) + } catch (error) { + return false + } + } + const schnorr = { + Signature: SchnorrSignature, + getPublicKey: schnorrGetPublicKey, + sign: schnorrSign, + verify: schnorrVerify, + signSync: schnorrSignSync, + verifySync: schnorrVerifySync + } + Point.BASE._setWindowSize(8) + const crypto = { + node: nodeCrypto, + web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined + } + const TAGS = { + challenge: 'BIP0340/challenge', + aux: 'BIP0340/aux', + nonce: 'BIP0340/nonce' + } + const TAGGED_HASH_PREFIXES = {} + const utils = { + isValidPrivateKey(privateKey) { + try { + normalizePrivateKey(privateKey) + return true + } catch (error) { + return false + } + }, + privateAdd: (privateKey, tweak) => { + const p = normalizePrivateKey(privateKey) + const t = normalizePrivateKey(tweak) + return numTo32b(mod(p + t, CURVE.n)) + }, + privateNegate: privateKey => { + const p = normalizePrivateKey(privateKey) + return numTo32b(CURVE.n - p) + }, + pointAddScalar: (p, tweak, isCompressed) => { + const P = Point.fromHex(p) + const t = normalizePrivateKey(tweak) + const Q = Point.BASE.multiplyAndAddUnsafe(P, t, _1n) + if (!Q) throw new Error('Tweaked point at infinity') + return Q.toRawBytes(isCompressed) + }, + pointMultiply: (p, tweak, isCompressed) => { + const P = Point.fromHex(p) + const t = bytesToNumber(ensureBytes(tweak)) + return P.multiply(t).toRawBytes(isCompressed) + }, + hashToPrivateKey: hash => { + hash = ensureBytes(hash) + if (hash.length < 40 || hash.length > 1024) + throw new Error('Expected 40-1024 bytes of private key as per FIPS 186') + const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n + return numTo32b(num) + }, + randomBytes: (bytesLength = 32) => { + if (crypto.web) { + return crypto.web.getRandomValues(new Uint8Array(bytesLength)) + } else if (crypto.node) { + const {randomBytes} = crypto.node + return Uint8Array.from(randomBytes(bytesLength)) + } else { + throw new Error("The environment doesn't have randomBytes function") + } + }, + randomPrivateKey: () => { + return utils.hashToPrivateKey(utils.randomBytes(40)) + }, + bytesToHex, + hexToBytes, + concatBytes, + mod, + invert, + sha256: async (...messages) => { + if (crypto.web) { + const buffer = await crypto.web.subtle.digest( + 'SHA-256', + concatBytes(...messages) + ) + return new Uint8Array(buffer) + } else if (crypto.node) { + const {createHash} = crypto.node + const hash = createHash('sha256') + messages.forEach(m => hash.update(m)) + return Uint8Array.from(hash.digest()) + } else { + throw new Error("The environment doesn't have sha256 function") + } + }, + hmacSha256: async (key, ...messages) => { + if (crypto.web) { + const ckey = await crypto.web.subtle.importKey( + 'raw', + key, + {name: 'HMAC', hash: {name: 'SHA-256'}}, + false, + ['sign'] + ) + const message = concatBytes(...messages) + const buffer = await crypto.web.subtle.sign('HMAC', ckey, message) + return new Uint8Array(buffer) + } else if (crypto.node) { + const {createHmac} = crypto.node + const hash = createHmac('sha256', key) + messages.forEach(m => hash.update(m)) + return Uint8Array.from(hash.digest()) + } else { + throw new Error("The environment doesn't have hmac-sha256 function") + } + }, + sha256Sync: undefined, + hmacSha256Sync: undefined, + taggedHash: async (tag, ...messages) => { + let tagP = TAGGED_HASH_PREFIXES[tag] + if (tagP === undefined) { + const tagH = await utils.sha256( + Uint8Array.from(tag, c => c.charCodeAt(0)) + ) + tagP = concatBytes(tagH, tagH) + TAGGED_HASH_PREFIXES[tag] = tagP + } + return utils.sha256(tagP, ...messages) + }, + taggedHashSync: (tag, ...messages) => { + if (typeof utils.sha256Sync !== 'function') + throw new Error('utils.sha256Sync is undefined, you need to set it') + let tagP = TAGGED_HASH_PREFIXES[tag] + if (tagP === undefined) { + const tagH = utils.sha256Sync( + Uint8Array.from(tag, c => c.charCodeAt(0)) + ) + tagP = concatBytes(tagH, tagH) + TAGGED_HASH_PREFIXES[tag] = tagP + } + return utils.sha256Sync(tagP, ...messages) + }, + precompute(windowSize = 8, point = Point.BASE) { + const cached = point === Point.BASE ? point : new Point(point.x, point.y) + cached._setWindowSize(windowSize) + cached.multiply(_3n) + return cached + } + } + + exports.CURVE = CURVE + exports.Point = Point + exports.Signature = Signature + exports.getPublicKey = getPublicKey + exports.getSharedSecret = getSharedSecret + exports.recoverPublicKey = recoverPublicKey + exports.schnorr = schnorr + exports.sign = sign + exports.signSync = signSync + exports.utils = utils + exports.verify = verify + + Object.defineProperty(exports, '__esModule', {value: true}) +}) diff --git a/lnbits/extensions/cashu/static/js/utils.js b/lnbits/extensions/cashu/static/js/utils.js new file mode 100644 index 00000000..cf852b58 --- /dev/null +++ b/lnbits/extensions/cashu/static/js/utils.js @@ -0,0 +1,23 @@ +function splitAmount(value) { + const chunks = [] + for (let i = 0; i < 32; i++) { + const mask = 1 << i + if ((value & mask) !== 0) chunks.push(Math.pow(2, i)) + } + return chunks +} + +function bytesToNumber(bytes) { + return hexToNumber(nobleSecp256k1.utils.bytesToHex(bytes)) +} + +function bigIntStringify(key, value) { + return typeof value === 'bigint' ? value.toString() : value +} + +function hexToNumber(hex) { + if (typeof hex !== 'string') { + throw new TypeError('hexToNumber: expected string, got ' + typeof hex) + } + return BigInt(`0x${hex}`) +} diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py new file mode 100644 index 00000000..9de17a1c --- /dev/null +++ b/lnbits/extensions/cashu/tasks.py @@ -0,0 +1,33 @@ +import asyncio +import json + +from cashu.core.migrations import migrate_databases +from cashu.mint import migrations + +from lnbits.core.models import Payment +from lnbits.tasks import register_invoice_listener + +from . import db, ledger +from .crud import get_cashu + + +async def startup_cashu_mint(): + await migrate_databases(db, migrations) + await ledger.load_used_proofs() + await ledger.init_keysets(autosave=False) + pass + + +async def wait_for_paid_invoices(): + invoice_queue = asyncio.Queue() + register_invoice_listener(invoice_queue) + + while True: + payment = await invoice_queue.get() + await on_invoice_paid(payment) + + +async def on_invoice_paid(payment: Payment) -> None: + if payment.extra and not payment.extra.get("tag") == "cashu": + return + return diff --git a/lnbits/extensions/cashu/templates/cashu/_api_docs.html b/lnbits/extensions/cashu/templates/cashu/_api_docs.html new file mode 100644 index 00000000..f7bb19f6 --- /dev/null +++ b/lnbits/extensions/cashu/templates/cashu/_api_docs.html @@ -0,0 +1,80 @@ + + + + diff --git a/lnbits/extensions/cashu/templates/cashu/_cashu.html b/lnbits/extensions/cashu/templates/cashu/_cashu.html new file mode 100644 index 00000000..952fe7e1 --- /dev/null +++ b/lnbits/extensions/cashu/templates/cashu/_cashu.html @@ -0,0 +1,13 @@ + + + +

Create Cashu ecash mints and wallets.

+ Created by + arcbtc, + vlad, + calle. +
+
+
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html new file mode 100644 index 00000000..2599669c --- /dev/null +++ b/lnbits/extensions/cashu/templates/cashu/index.html @@ -0,0 +1,367 @@ +{% extends "base.html" %} {% from "macros.jinja" import window_vars with context +%} {% block page %} +
+
+ + + Cashu mint and wallet +

+

+ Here you can create multiple cashu mints that you can share. Each mint + can service many users but all ecash tokens of a mint are only valid + inside that mint and not across different mints. To exchange funds + between mints, use Lightning payments. +

+ Important +

+

+ If you are the operator of this LNbits instance, make sure to set + CASHU_PRIVATE_KEY="randomkey" in your configuration file. Do not + create mints before setting the key and do not change the key once + set. +

+
+
+ + + +
+
+
Mints
+
+
+ Export to CSV +
+
+ + {% raw %} + + + + {% endraw %} + + New Mint +
+
+
+ +
+ + +
{{SITE_TITLE}} Cashu extension
+
+ + + + {% include "cashu/_api_docs.html" %} + + {% include "cashu/_cashu.html" %} + + +
+
+ + + + + + + +
+ Create Mint + + Cancel +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }} + +{% endblock %} diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html new file mode 100644 index 00000000..ee6ab606 --- /dev/null +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -0,0 +1,76 @@ +{% extends "public.html" %} {% block page %} +
+
+ + +
+ +

{{ mint_name }}

+ Open wallet +
+
+
+ + +
Read the following carefully!
+

+ This is a + Cashu + mint. Cashu is an ecash system for Bitcoin. +

+

+ Open this page in your native browser
+ Before you continue to the wallet, make sure to open this page in your + device's native browser application (Safari for iOS, Chrome for + Android). Do not use Cashu in an embedded browser that opens when you + click a link in a messenger. +

+

+ Add wallet to home screen
+ You can add Cashu to your home screen as a progressive web app (PWA). + After opening the wallet in your browser (click the link above), on + Android (Chrome), click the menu at the upper right. On iOS (Safari), + click the share button. Now press the Add to Home screen button. +

+

+ Backup your wallet
+ Ecash is a bearer asset. That means losing access to your wallet will + make you lose your funds. The wallet stores ecash tokens on your + device's database. If you lose the link or delete your your data + without backing up, you will lose your tokens. Press the Backup button + in the wallet to download a copy of your tokens. +

+

+ This service is in BETA
+ We hold no responsibility for people losing access to funds. Use at + your own risk! +

+
+
+
+ + {% endblock %} {% block scripts %} + + + + {% endblock %} +
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html new file mode 100644 index 00000000..a133f592 --- /dev/null +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -0,0 +1,2337 @@ +{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu +{% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block +page_container %} + + +
+
+ + +
+
+
+ Get invoice + +
+
+

+
+ {% raw %} {{getBalance()}} + {{tickershort}}{% endraw %} +
+

+
+
+ Pay invoice + +
+
+
+
+
+
+

+
+ {% raw %} {{getBalance()}} + {{tickershort}}{% endraw %} +
+

+
+
+
+
+
+ + + +
+
+ Get Ecash +
+
+
+ + Pay Ecash +
+
+ + + + + + + + + + + + + {% raw %} + + {% endraw %} + + + + + + + + {% raw %} + + {% endraw %} + + + + + + + + {% raw %} + + {% endraw %} + + + +
+
+ +
+ + Warning + BackupDownload wallet backup +
+
+ + + + + + + + + + + + +
+
+ {% raw %} {{ + parseFloat(String(payInvoiceData.invoice.fsat).replaceAll(",", + "")) / 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %} +
+
+ {{ payInvoiceData.invoice.fsat }}{% endraw %} + {{LNBITS_DENOMINATION}} {% raw %} +
+ +

+ Description: {{ + payInvoiceData.invoice.description }}
+ Expire date: {{ payInvoiceData.invoice.expireDate + }}
+ Hash: {{ payInvoiceData.invoice.hash }} +

+ {% endraw %} +
+ Pay + Cancel +
+
+ Not enough funds! + Cancel +
+
+
+ {% raw %} + +

+ Authenticate with {{ payInvoiceData.lnurlauth.domain }}? +

+ +

+ For every website and for every LNbits wallet, a new keypair + will be deterministically generated so your identity can't be + tied to your LNbits wallet or linked across websites. No other + data will be shared with {{ payInvoiceData.lnurlauth.domain }}. +

+

+ Your public key for + {{ payInvoiceData.lnurlauth.domain }} is: +

+

+ + {{ payInvoiceData.lnurlauth.pubkey }} + +

+
+ Login + Cancel +
+
+ {% endraw %} +
+
+ {% raw %} + +

+ {{ payInvoiceData.lnurlpay.domain }} is requesting {{ + payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }} + {{LNBITS_DENOMINATION}} + +
+ and a {{payInvoiceData.lnurlpay.commentAllowed}}-char comment +
+

+

+ {{ payInvoiceData.lnurlpay.targetUser || + payInvoiceData.lnurlpay.domain }} + is requesting
+ between + {{ payInvoiceData.lnurlpay.minSendable | msatoshiFormat }} + and + {{ payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }} + {% endraw %} {{LNBITS_DENOMINATION}} {% raw %} + +
+ and a {{payInvoiceData.lnurlpay.commentAllowed}}-char comment +
+

+ +
+

+ {{ payInvoiceData.lnurlpay.description }} +

+

+ +

+
+
+
+ {% endraw %} + + {% raw %} +
+
+ +
+
+
+ Send {{LNBITS_DENOMINATION}} + Cancel +
+
+ {% endraw %} +
+
+ + + +
+ Enter + + + Close +
+
+
+ + + +
+ + Cancel + +
+
+
+
+
+ + + +
+ +
+
+ Cancel +
+
+
+ + + +
Warning
+

+ Bookmark this page and backup your tokens! + Ecash is a bearer asset, meaning losing access to this wallet will + mean you will lose the funds. This wallet stores ecash tokens in its + database. If you lose the link or delete your your data without + backing up, you will lose your tokens. Press the Backup button to + download a copy of your tokens. +

+

+ Add to home screen. + You can add Cashu to your home screen as a progressive web app + (PWA). On Android Chrome, click the hamburger menu at the upper + right. On iOS Safari, click the share button. Now press the Add to + Home screen button. +

+

+ This service is in BETA! We hold no responsibility + for people losing access to funds. Use at your own risk! +

+
+ Copy wallet URL + I understand +
+
+
+ + + +
+
+
+ Create a Lightning invoice +
+
+ + +
+ +
+ Copy invoice + Create Invoice + Close +
+
+
+ + + +
+
+
+ How much would you like to send? +
+
+ + +
+
+
+ + + + + + +
+ +
+
+ Send Tokens + +
+ Copy token + Copy link +
+ + Close +
+
+
+ + + +
+
+
+ Receive Cashu tokens +
+
+ +
+ +
+ Receive Tokens + + Close +
+
+
+
+
+
+{% endblock %} {% block styles %} + +{% endblock %} {% block scripts %} + + + + + +{% endblock %} diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py new file mode 100644 index 00000000..0de791c4 --- /dev/null +++ b/lnbits/extensions/cashu/views.py @@ -0,0 +1,224 @@ +from http import HTTPStatus + +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.exceptions import HTTPException +from starlette.responses import HTMLResponse + +from lnbits.core.models import User +from lnbits.decorators import check_user_exists + +from . import cashu_ext, cashu_renderer +from .crud import get_cashu + +templates = Jinja2Templates(directory="templates") + + +@cashu_ext.get("/", response_class=HTMLResponse) +async def index( + request: Request, + user: User = Depends(check_user_exists), # type: ignore +): + return cashu_renderer().TemplateResponse( + "cashu/index.html", {"request": request, "user": user.dict()} + ) + + +@cashu_ext.get("/wallet") +async def wallet(request: Request, mint_id: str): + return cashu_renderer().TemplateResponse( + "cashu/wallet.html", + { + "request": request, + "web_manifest": f"/cashu/manifest/{mint_id}.webmanifest", + }, + ) + + +@cashu_ext.get("/mint/{mintID}") +async def cashu(request: Request, mintID): + cashu = await get_cashu(mintID) + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." + ) + return cashu_renderer().TemplateResponse( + "cashu/mint.html", + {"request": request, "mint_name": cashu.name, "mint_id": mintID}, + ) + + +@cashu_ext.get("/manifest/{cashu_id}.webmanifest") +async def manifest(cashu_id: str): + cashu = await get_cashu(cashu_id) + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." + ) + + return { + "short_name": "Cashu", + "name": "Cashu" + " - " + cashu.name, + "icons": [ + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png", + "type": "image/png", + "sizes": "512x512", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png", + "type": "image/png", + "sizes": "96x96", + }, + ], + "id": "/cashu/wallet?mint_id=" + cashu_id, + "start_url": "/cashu/wallet?mint_id=" + cashu_id, + "background_color": "#1F2234", + "description": "Cashu ecash wallet", + "display": "standalone", + "scope": "/cashu/", + "theme_color": "#1F2234", + "protocol_handlers": [ + {"protocol": "cashu", "url": "&recv_token=%s"}, + {"protocol": "lightning", "url": "&lightning=%s"}, + ], + "shortcuts": [ + { + "name": "Cashu" + " - " + cashu.name, + "short_name": "Cashu", + "description": "Cashu" + " - " + cashu.name, + "url": "/cashu/wallet?mint_id=" + cashu_id, + "icons": [ + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-512-512.png", + "sizes": "512x512", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-192-192.png", + "sizes": "192x192", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-144-144.png", + "sizes": "144x144", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-96-96.png", + "sizes": "96x96", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-72-72.png", + "sizes": "72x72", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/android/android-launchericon-48-48.png", + "sizes": "48x48", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/16.png", + "sizes": "16x16", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/20.png", + "sizes": "20x20", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/29.png", + "sizes": "29x29", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/32.png", + "sizes": "32x32", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/40.png", + "sizes": "40x40", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/50.png", + "sizes": "50x50", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/57.png", + "sizes": "57x57", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/58.png", + "sizes": "58x58", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/60.png", + "sizes": "60x60", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/64.png", + "sizes": "64x64", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/72.png", + "sizes": "72x72", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/76.png", + "sizes": "76x76", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/80.png", + "sizes": "80x80", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/87.png", + "sizes": "87x87", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/100.png", + "sizes": "100x100", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/114.png", + "sizes": "114x114", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/120.png", + "sizes": "120x120", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/128.png", + "sizes": "128x128", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/144.png", + "sizes": "144x144", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/152.png", + "sizes": "152x152", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/167.png", + "sizes": "167x167", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/180.png", + "sizes": "180x180", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/192.png", + "sizes": "192x192", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/256.png", + "sizes": "256x256", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/512.png", + "sizes": "512x512", + }, + { + "src": "https://github.com/cashubtc/cashu-ui/raw/main/ui/icons/circle/ios/1024.png", + "sizes": "1024x1024", + }, + ], + } + ], + } diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py new file mode 100644 index 00000000..ad253abf --- /dev/null +++ b/lnbits/extensions/cashu/views_api.py @@ -0,0 +1,382 @@ +import json +import math +from http import HTTPStatus +from typing import Dict, List, Union + +import httpx + +# -------- cashu imports +from cashu.core.base import ( + BlindedSignature, + CheckFeesRequest, + CheckFeesResponse, + CheckRequest, + GetMeltResponse, + GetMintResponse, + Invoice, + MeltRequest, + MintRequest, + PostSplitResponse, + Proof, + SplitRequest, +) +from fastapi import Query +from fastapi.params import Depends +from lnurl import decode as decode_lnurl +from loguru import logger +from secp256k1 import PublicKey +from starlette.exceptions import HTTPException + +from lnbits import bolt11 +from lnbits.core.crud import check_internal, get_user +from lnbits.core.services import ( + check_transaction_status, + create_invoice, + fee_reserve, + pay_invoice, +) +from lnbits.core.views.api import api_payment +from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key +from lnbits.helpers import urlsafe_short_hash +from lnbits.wallets.base import PaymentStatus + +from . import cashu_ext, ledger +from .crud import create_cashu, delete_cashu, get_cashu, get_cashus +from .models import Cashu + +# --------- extension imports + + +LIGHTNING = True + +######################################## +############### LNBITS MINTS ########### +######################################## + + +@cashu_ext.get("/api/v1/mints", status_code=HTTPStatus.OK) +async def api_cashus( + all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore +): + """ + Get all mints of this wallet. + """ + wallet_ids = [wallet.wallet.id] + if all_wallets: + user = await get_user(wallet.wallet.user) + if user: + wallet_ids = user.wallet_ids + + return [cashu.dict() for cashu in await get_cashus(wallet_ids)] + + +@cashu_ext.post("/api/v1/mints", status_code=HTTPStatus.CREATED) +async def api_cashu_create( + data: Cashu, + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore +): + """ + Create a new mint for this wallet. + """ + cashu_id = urlsafe_short_hash() + # generate a new keyset in cashu + keyset = await ledger.load_keyset(cashu_id) + + cashu = await create_cashu( + cashu_id=cashu_id, keyset_id=keyset.id, wallet_id=wallet.wallet.id, data=data + ) + logger.debug(cashu) + return cashu.dict() + + +@cashu_ext.delete("/api/v1/mints/{cashu_id}") +async def api_cashu_delete( + cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) # type: ignore +): + """ + Delete an existing cashu mint. + """ + cashu = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Cashu mint does not exist." + ) + + if cashu.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu mint." + ) + + await delete_cashu(cashu_id) + raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + + +####################################### +########### CASHU ENDPOINTS ########### +####################################### + + +@cashu_ext.get("/api/v1/{cashu_id}/keys", status_code=HTTPStatus.OK) +async def keys(cashu_id: str = Query(None)) -> dict[int, str]: + """Get the public keys of the mint""" + cashu: Union[Cashu, None] = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + + return ledger.get_keyset(keyset_id=cashu.keyset_id) + + +@cashu_ext.get("/api/v1/{cashu_id}/keysets", status_code=HTTPStatus.OK) +async def keysets(cashu_id: str = Query(None)) -> dict[str, list[str]]: + """Get the public keys of the mint""" + cashu: Union[Cashu, None] = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + + return {"keysets": [cashu.keyset_id]} + + +@cashu_ext.get("/api/v1/{cashu_id}/mint") +async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintResponse: + """ + Request minting of new tokens. The mint responds with a Lightning invoice. + This endpoint can be used for a Lightning invoice UX flow. + + Call `POST /mint` after paying the invoice. + """ + cashu: Union[Cashu, None] = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + + # create an invoice that the wallet needs to pay + try: + payment_hash, payment_request = await create_invoice( + wallet_id=cashu.wallet, + amount=amount, + memo=f"{cashu.name}", + extra={"tag": "cashu"}, + ) + invoice = Invoice( + amount=amount, pr=payment_request, hash=payment_hash, issued=False + ) + # await store_lightning_invoice(cashu_id, invoice) + await ledger.crud.store_lightning_invoice(invoice=invoice, db=ledger.db) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) + + print(f"Lightning invoice: {payment_request}") + resp = GetMintResponse(pr=payment_request, hash=payment_hash) + # return {"pr": payment_request, "hash": payment_hash} + return resp + + +@cashu_ext.post("/api/v1/{cashu_id}/mint") +async def mint_coins( + data: MintRequest, + cashu_id: str = Query(None), + payment_hash: str = Query(None), +) -> List[BlindedSignature]: + """ + Requests the minting of tokens belonging to a paid payment request. + Call this endpoint after `GET /mint`. + """ + cashu: Union[Cashu, None] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + + if LIGHTNING: + invoice: Invoice = await ledger.crud.get_lightning_invoice( + db=ledger.db, hash=payment_hash + ) + if invoice is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Mint does not know this invoice.", + ) + if invoice.issued == True: + raise HTTPException( + status_code=HTTPStatus.PAYMENT_REQUIRED, + detail="Tokens already issued for this invoice.", + ) + + total_requested = sum([bm.amount for bm in data.blinded_messages]) + if total_requested > invoice.amount: + raise HTTPException( + status_code=HTTPStatus.PAYMENT_REQUIRED, + detail=f"Requested amount too high: {total_requested}. Invoice amount: {invoice.amount}", + ) + + status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash) + + if status.paid != True: + raise HTTPException( + status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid." + ) + try: + keyset = ledger.keysets.keysets[cashu.keyset_id] + + promises = await ledger._generate_promises( + B_s=data.blinded_messages, keyset=keyset + ) + assert len(promises), HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="No promises returned." + ) + await ledger.crud.update_lightning_invoice( + db=ledger.db, hash=payment_hash, issued=True + ) + + return promises + except Exception as e: + logger.error(e) + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) + + +@cashu_ext.post("/api/v1/{cashu_id}/melt") +async def melt_coins( + payload: MeltRequest, cashu_id: str = Query(None) +) -> GetMeltResponse: + """Invalidates proofs and pays a Lightning invoice.""" + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + proofs = payload.proofs + invoice = payload.invoice + + # !!!!!!! MAKE SURE THAT PROOFS ARE ONLY FROM THIS CASHU KEYSET ID + # THIS IS NECESSARY BECAUSE THE CASHU BACKEND WILL ACCEPT ANY VALID + # TOKENS + assert all([p.id == cashu.keyset_id for p in proofs]), HTTPException( + status_code=HTTPStatus.METHOD_NOT_ALLOWED, + detail="Error: Tokens are from another mint.", + ) + + assert all([ledger._verify_proof(p) for p in proofs]), HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="Could not verify proofs.", + ) + + total_provided = sum([p["amount"] for p in proofs]) + invoice_obj = bolt11.decode(invoice) + amount = math.ceil(invoice_obj.amount_msat / 1000) + + internal_checking_id = await check_internal(invoice_obj.payment_hash) + + if not internal_checking_id: + fees_msat = fee_reserve(invoice_obj.amount_msat) + else: + fees_msat = 0 + assert total_provided >= amount + fees_msat / 1000, Exception( + f"Provided proofs ({total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)." + ) + + await pay_invoice( + wallet_id=cashu.wallet, + payment_request=invoice, + description=f"pay cashu invoice", + extra={"tag": "cashu", "cahsu_name": cashu.name}, + ) + + status: PaymentStatus = await check_transaction_status( + cashu.wallet, invoice_obj.payment_hash + ) + if status.paid == True: + await ledger._invalidate_proofs(proofs) + return GetMeltResponse(paid=status.paid, preimage=status.preimage) + + +@cashu_ext.post("/api/v1/{cashu_id}/check") +async def check_spendable( + payload: CheckRequest, cashu_id: str = Query(None) +) -> Dict[int, bool]: + """Check whether a secret has been spent already or not.""" + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + return await ledger.check_spendable(payload.proofs) + + +@cashu_ext.post("/api/v1/{cashu_id}/checkfees") +async def check_fees( + payload: CheckFeesRequest, cashu_id: str = Query(None) +) -> CheckFeesResponse: + """ + Responds with the fees necessary to pay a Lightning invoice. + Used by wallets for figuring out the fees they need to supply. + This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu). + """ + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + invoice_obj = bolt11.decode(payload.pr) + internal_checking_id = await check_internal(invoice_obj.payment_hash) + + if not internal_checking_id: + fees_msat = fee_reserve(invoice_obj.amount_msat) + else: + fees_msat = 0 + return CheckFeesResponse(fee=fees_msat / 1000) + + +@cashu_ext.post("/api/v1/{cashu_id}/split") +async def split( + payload: SplitRequest, cashu_id: str = Query(None) +) -> PostSplitResponse: + """ + Requetst a set of tokens with amount "total" to be split into two + newly minted sets with amount "split" and "total-split". + """ + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + proofs = payload.proofs + + # !!!!!!! MAKE SURE THAT PROOFS ARE ONLY FROM THIS CASHU KEYSET ID + # THIS IS NECESSARY BECAUSE THE CASHU BACKEND WILL ACCEPT ANY VALID + # TOKENS + if not all([p.id == cashu.keyset_id for p in proofs]): + raise HTTPException( + status_code=HTTPStatus.METHOD_NOT_ALLOWED, + detail="Error: Tokens are from another mint.", + ) + + amount = payload.amount + outputs = payload.outputs.blinded_messages + assert outputs, Exception("no outputs provided.") + split_return = None + try: + keyset = ledger.keysets.keysets[cashu.keyset_id] + split_return = await ledger.split(proofs, amount, outputs, keyset) + except Exception as exc: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail=str(exc), + ) + if not split_return: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="there was an error with the split", + ) + frst_promises, scnd_promises = split_return + resp = PostSplitResponse(fst=frst_promises, snd=scnd_promises) + return resp diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 83876160..9042ece0 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -20,6 +20,8 @@ class Extension(NamedTuple): icon: Optional[str] = None contributors: Optional[List[str]] = None hidden: bool = False + migration_module: Optional[str] = None + db_name: Optional[str] = None class ExtensionManager: @@ -66,6 +68,8 @@ class ExtensionManager: config.get("icon"), config.get("contributors"), config.get("hidden") or False, + config.get("migration_module"), + config.get("db_name"), ) ) diff --git a/poetry.lock b/poetry.lock index 5b283d75..1df342c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,7 +8,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "anyio" -version = "3.6.1" +version = "3.6.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false @@ -22,7 +22,7 @@ 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)"] +trio = ["trio (>=0.16,<0.22)"] [[package]] name = "asgiref" @@ -59,17 +59,17 @@ typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""} [[package]] name = "attrs" -version = "21.2.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "base58" @@ -100,11 +100,11 @@ python-versions = "*" [[package]] name = "black" -version = "22.8.0" +version = "22.10.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" @@ -121,6 +121,60 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "cashu" +version = "0.5.4" +description = "Ecash wallet and mint with Bitcoin Lightning support" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +colorama = {version = "0.4.5", markers = "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\""} +ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""} +iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +py = {version = "1.11.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pycparser = {version = "2.21", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pydantic = {version = "1.10.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pyparsing = {version = "3.0.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pysocks = {version = "1.7.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pytest = {version = "7.1.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +pytest-asyncio = {version = "0.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""} +uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""} +zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""} + [[package]] name = "Cerberus" version = "1.3.4" @@ -134,15 +188,15 @@ setuptools = "*" [[package]] name = "certifi" -version = "2021.5.30" +version = "2022.9.24" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "cffi" -version = "1.15.0" +version = "1.15.1" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -153,7 +207,7 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "2.0.6" +version = "2.0.12" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -164,7 +218,7 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.1" +version = "8.0.4" description = "Composable command line interface toolkit" category = "main" optional = false @@ -229,7 +283,7 @@ test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0 [[package]] name = "ecdsa" -version = "0.17.0" +version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" category = "main" optional = false @@ -260,7 +314,7 @@ python-versions = "*" [[package]] name = "environs" -version = "9.3.3" +version = "9.5.0" description = "simplified environment variable parsing" category = "main" optional = false @@ -271,14 +325,14 @@ marshmallow = ">=3.0.0" python-dotenv = "*" [package.extras] -dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] django = ["dj-database-url", "dj-email-url", "django-cache-url"] -lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] [[package]] name = "fastapi" -version = "0.78.0" +version = "0.83.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" category = "main" optional = false @@ -290,13 +344,13 @@ starlette = "0.19.1" [package.extras] all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"] -dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"] +dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"] doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"] -test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] [[package]] name = "grpcio" -version = "1.49.1" +version = "1.50.0" description = "HTTP/2-based RPC framework" category = "main" optional = false @@ -306,7 +360,7 @@ python-versions = ">=3.7" six = ">=1.5.2" [package.extras] -protobuf = ["grpcio-tools (>=1.49.1)"] +protobuf = ["grpcio-tools (>=1.50.0)"] [[package]] name = "h11" @@ -367,7 +421,7 @@ socks = ["socksio (>=1.0.0,<2.0.0)"] [[package]] name = "idna" -version = "3.2" +version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -375,26 +429,26 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.8.1" +version = "5.0.0" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-perf (>=0.9.2)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -441,7 +495,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "loguru" -version = "0.5.3" +version = "0.6.0" description = "Python logging made (stupidly) simple" category = "main" optional = false @@ -452,7 +506,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (>=2.2.1)", "black (>=19.10b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"] +dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"] [[package]] name = "MarkupSafe" @@ -464,7 +518,7 @@ python-versions = ">=3.6" [[package]] name = "marshmallow" -version = "3.17.0" +version = "3.18.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." category = "main" optional = false @@ -474,9 +528,9 @@ python-versions = ">=3.7" packaging = ">=17.0" [package.extras] -dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.8)", "sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)"] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -521,11 +575,11 @@ python-versions = "*" [[package]] name = "outcome" -version = "1.1.0" +version = "1.2.0" description = "Capture the outcome of Python function calls." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" @@ -554,7 +608,7 @@ six = "*" [[package]] name = "pathspec" -version = "0.10.1" +version = "0.10.2" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -562,21 +616,21 @@ python-versions = ">=3.7" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.5.4" +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" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] +test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -589,7 +643,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "4.21.7" +version = "4.21.9" description = "" category = "main" optional = false @@ -607,7 +661,7 @@ python-versions = ">=3.6" name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -629,14 +683,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pydantic" -version = "1.8.2" -description = "Data validation and settings management using python 3.6 type hinting" +version = "1.10.2" +description = "Data validation and settings management using python type hints" category = "main" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7" [package.dependencies] -typing-extensions = ">=3.7.4.3" +typing-extensions = ">=4.1.0" [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] @@ -732,7 +786,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "pytest" version = "7.1.3" description = "pytest: simple powerful testing with Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -753,7 +807,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2. name = "pytest-asyncio" version = "0.19.0" description = "Pytest support for asyncio" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -779,13 +833,21 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "python-bitcoinlib" +version = "0.11.2" +description = "The Swiss Army Knife of the Bitcoin protocol." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "python-dotenv" -version = "0.19.0" +version = "0.21.0" description = "Read key-value pairs from a .env file and set them as environment variables" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.extras] cli = ["click (>=5.0)"] @@ -812,6 +874,24 @@ six = ">=1.8.0" [package.extras] test = ["ipython", "mock", "pytest (>=3.0.5)"] +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + [[package]] name = "rfc3986" version = "1.5.0" @@ -839,7 +919,7 @@ cffi = ">=1.3.0" [[package]] name = "setuptools" -version = "65.4.1" +version = "65.6.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false @@ -847,7 +927,7 @@ python-versions = ">=3.7" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -868,15 +948,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "sniffio" -version = "1.2.0" +version = "1.3.0" description = "Sniff out which async library your code is running under" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [[package]] name = "SQLAlchemy" -version = "1.3.23" +version = "1.3.24" description = "Database Abstraction Library" category = "main" optional = false @@ -939,7 +1019,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -953,7 +1033,7 @@ python-versions = ">=3.6" [[package]] name = "types-protobuf" -version = "3.20.4" +version = "3.20.4.6" description = "Typing stubs for protobuf" category = "dev" optional = false @@ -961,15 +1041,28 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.10.0.2" -description = "Backported and Experimental Type Hints for Python 3.5+" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" + +[[package]] +name = "urllib3" +version = "1.26.12" +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.*, <4" + +[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)"] [[package]] name = "uvicorn" -version = "0.18.1" +version = "0.18.3" description = "The lightning-fast ASGI server." category = "main" optional = false @@ -981,7 +1074,7 @@ h11 = ">=0.8" typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"] +standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"] [[package]] name = "uvloop" @@ -1038,20 +1131,20 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] [[package]] name = "zipp" -version = "3.5.0" +version = "3.9.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7" -content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42" +content-hash = "d8d786accf4ece1207e2f54ac37aba6601d089f9c1ca1269bf235d67b8e3e2a7" [metadata.files] aiofiles = [ @@ -1059,8 +1152,8 @@ aiofiles = [ {file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"}, ] anyio = [ - {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, - {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, ] asgiref = [ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, @@ -1075,8 +1168,8 @@ async-timeout = [ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, ] attrs = [ - {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, - {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] base58 = [ {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, @@ -1092,96 +1185,112 @@ bitstring = [ {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"}, ] black = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, +] +cashu = [ + {file = "cashu-0.5.4-py3-none-any.whl", hash = "sha256:de02245387fe0affaa1a02255ab9592c4115fd1deae57ccae3eee4610ab40ebb"}, + {file = "cashu-0.5.4.tar.gz", hash = "sha256:9fbfe21828282697bcc8ab7690c23509b867c9851beb525efd04ed386b84db10"}, ] Cerberus = [ {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"}, ] certifi = [ - {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, - {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, ] cffi = [ - {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, - {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, - {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, - {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, - {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, - {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, - {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, - {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, - {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, - {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, - {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, - {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, - {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, - {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, - {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, - {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, - {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"}, - {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"}, + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, ] click = [ - {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, - {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, + {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, + {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, ] coincurve = [ {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"}, @@ -1298,8 +1407,8 @@ cryptography = [ {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"}, ] ecdsa = [ - {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, - {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"}, + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, ] embit = [ {file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"}, @@ -1310,59 +1419,59 @@ enum34 = [ {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, ] environs = [ - {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"}, - {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"}, + {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"}, + {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"}, ] fastapi = [ - {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"}, - {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"}, + {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"}, + {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"}, ] grpcio = [ - {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"}, - {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"}, - {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"}, - {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"}, - {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"}, - {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"}, - {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"}, - {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"}, - {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"}, - {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"}, - {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"}, - {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"}, - {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"}, - {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"}, - {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"}, - {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"}, - {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"}, - {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"}, - {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"}, - {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"}, - {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"}, - {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"}, - {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"}, - {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"}, - {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"}, - {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"}, - {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"}, - {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"}, - {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"}, - {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"}, - {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"}, - {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"}, - {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"}, - {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"}, - {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"}, - {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"}, - {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"}, - {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"}, - {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"}, - {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"}, - {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"}, - {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"}, - {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"}, - {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"}, - {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"}, + {file = "grpcio-1.50.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:906f4d1beb83b3496be91684c47a5d870ee628715227d5d7c54b04a8de802974"}, + {file = "grpcio-1.50.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:2d9fd6e38b16c4d286a01e1776fdf6c7a4123d99ae8d6b3f0b4a03a34bf6ce45"}, + {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:4b123fbb7a777a2fedec684ca0b723d85e1d2379b6032a9a9b7851829ed3ca9a"}, + {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2f77a90ba7b85bfb31329f8eab9d9540da2cf8a302128fb1241d7ea239a5469"}, + {file = "grpcio-1.50.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eea18a878cffc804506d39c6682d71f6b42ec1c151d21865a95fae743fda500"}, + {file = "grpcio-1.50.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b71916fa8f9eb2abd93151fafe12e18cebb302686b924bd4ec39266211da525"}, + {file = "grpcio-1.50.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:95ce51f7a09491fb3da8cf3935005bff19983b77c4e9437ef77235d787b06842"}, + {file = "grpcio-1.50.0-cp310-cp310-win32.whl", hash = "sha256:f7025930039a011ed7d7e7ef95a1cb5f516e23c5a6ecc7947259b67bea8e06ca"}, + {file = "grpcio-1.50.0-cp310-cp310-win_amd64.whl", hash = "sha256:05f7c248e440f538aaad13eee78ef35f0541e73498dd6f832fe284542ac4b298"}, + {file = "grpcio-1.50.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:ca8a2254ab88482936ce941485c1c20cdeaef0efa71a61dbad171ab6758ec998"}, + {file = "grpcio-1.50.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3b611b3de3dfd2c47549ca01abfa9bbb95937eb0ea546ea1d762a335739887be"}, + {file = "grpcio-1.50.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a4cd8cb09d1bc70b3ea37802be484c5ae5a576108bad14728f2516279165dd7"}, + {file = "grpcio-1.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:156f8009e36780fab48c979c5605eda646065d4695deea4cfcbcfdd06627ddb6"}, + {file = "grpcio-1.50.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de411d2b030134b642c092e986d21aefb9d26a28bf5a18c47dd08ded411a3bc5"}, + {file = "grpcio-1.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d144ad10eeca4c1d1ce930faa105899f86f5d99cecfe0d7224f3c4c76265c15e"}, + {file = "grpcio-1.50.0-cp311-cp311-win32.whl", hash = "sha256:92d7635d1059d40d2ec29c8bf5ec58900120b3ce5150ef7414119430a4b2dd5c"}, + {file = "grpcio-1.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce8513aee0af9c159319692bfbf488b718d1793d764798c3d5cff827a09e25ef"}, + {file = "grpcio-1.50.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:8e8999a097ad89b30d584c034929f7c0be280cd7851ac23e9067111167dcbf55"}, + {file = "grpcio-1.50.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:a50a1be449b9e238b9bd43d3857d40edf65df9416dea988929891d92a9f8a778"}, + {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:cf151f97f5f381163912e8952eb5b3afe89dec9ed723d1561d59cabf1e219a35"}, + {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a23d47f2fc7111869f0ff547f771733661ff2818562b04b9ed674fa208e261f4"}, + {file = "grpcio-1.50.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84d04dec64cc4ed726d07c5d17b73c343c8ddcd6b59c7199c801d6bbb9d9ed1"}, + {file = "grpcio-1.50.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:67dd41a31f6fc5c7db097a5c14a3fa588af54736ffc174af4411d34c4f306f68"}, + {file = "grpcio-1.50.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d4c8e73bf20fb53fe5a7318e768b9734cf122fe671fcce75654b98ba12dfb75"}, + {file = "grpcio-1.50.0-cp37-cp37m-win32.whl", hash = "sha256:7489dbb901f4fdf7aec8d3753eadd40839c9085967737606d2c35b43074eea24"}, + {file = "grpcio-1.50.0-cp37-cp37m-win_amd64.whl", hash = "sha256:531f8b46f3d3db91d9ef285191825d108090856b3bc86a75b7c3930f16ce432f"}, + {file = "grpcio-1.50.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:d534d169673dd5e6e12fb57cc67664c2641361e1a0885545495e65a7b761b0f4"}, + {file = "grpcio-1.50.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:1d8d02dbb616c0a9260ce587eb751c9c7dc689bc39efa6a88cc4fa3e9c138a7b"}, + {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:baab51dcc4f2aecabf4ed1e2f57bceab240987c8b03533f1cef90890e6502067"}, + {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40838061e24f960b853d7bce85086c8e1b81c6342b1f4c47ff0edd44bbae2722"}, + {file = "grpcio-1.50.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:931e746d0f75b2a5cff0a1197d21827a3a2f400c06bace036762110f19d3d507"}, + {file = "grpcio-1.50.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15f9e6d7f564e8f0776770e6ef32dac172c6f9960c478616c366862933fa08b4"}, + {file = "grpcio-1.50.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a4c23e54f58e016761b576976da6a34d876420b993f45f66a2bfb00363ecc1f9"}, + {file = "grpcio-1.50.0-cp38-cp38-win32.whl", hash = "sha256:3e4244c09cc1b65c286d709658c061f12c61c814be0b7030a2d9966ff02611e0"}, + {file = "grpcio-1.50.0-cp38-cp38-win_amd64.whl", hash = "sha256:8e69aa4e9b7f065f01d3fdcecbe0397895a772d99954bb82eefbb1682d274518"}, + {file = "grpcio-1.50.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:af98d49e56605a2912cf330b4627e5286243242706c3a9fa0bcec6e6f68646fc"}, + {file = "grpcio-1.50.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:080b66253f29e1646ac53ef288c12944b131a2829488ac3bac8f52abb4413c0d"}, + {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:ab5d0e3590f0a16cb88de4a3fa78d10eb66a84ca80901eb2c17c1d2c308c230f"}, + {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb11464f480e6103c59d558a3875bd84eed6723f0921290325ebe97262ae1347"}, + {file = "grpcio-1.50.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e07fe0d7ae395897981d16be61f0db9791f482f03fee7d1851fe20ddb4f69c03"}, + {file = "grpcio-1.50.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d75061367a69808ab2e84c960e9dce54749bcc1e44ad3f85deee3a6c75b4ede9"}, + {file = "grpcio-1.50.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ae23daa7eda93c1c49a9ecc316e027ceb99adbad750fbd3a56fa9e4a2ffd5ae0"}, + {file = "grpcio-1.50.0-cp39-cp39-win32.whl", hash = "sha256:177afaa7dba3ab5bfc211a71b90da1b887d441df33732e94e26860b3321434d9"}, + {file = "grpcio-1.50.0-cp39-cp39-win_amd64.whl", hash = "sha256:ea8ccf95e4c7e20419b7827aa5b6da6f02720270686ac63bd3493a651830235c"}, + {file = "grpcio-1.50.0.tar.gz", hash = "sha256:12b479839a5e753580b5e6053571de14006157f2ef9b71f38c56dc9b23b95ad6"}, ] h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, @@ -1413,12 +1522,12 @@ httpx = [ {file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"}, ] idna = [ - {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, - {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, - {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, + {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, + {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1437,8 +1546,8 @@ lnurl = [ {file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"}, ] loguru = [ - {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"}, - {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"}, + {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, + {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, ] MarkupSafe = [ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, @@ -1512,8 +1621,8 @@ MarkupSafe = [ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] marshmallow = [ - {file = "marshmallow-3.17.0-py3-none-any.whl", hash = "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb"}, - {file = "marshmallow-3.17.0.tar.gz", hash = "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"}, + {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"}, + {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"}, ] mock = [ {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, @@ -1549,8 +1658,8 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] outcome = [ - {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"}, - {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"}, + {file = "outcome-1.2.0-py2.py3-none-any.whl", hash = "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"}, + {file = "outcome-1.2.0.tar.gz", hash = "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672"}, ] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, @@ -1561,32 +1670,32 @@ pathlib2 = [ {file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"}, ] pathspec = [ - {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, - {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, + {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"}, + {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"}, ] platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, + {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] protobuf = [ - {file = "protobuf-4.21.7-cp310-abi3-win32.whl", hash = "sha256:c7cb105d69a87416bd9023e64324e1c089593e6dae64d2536f06bcbe49cd97d8"}, - {file = "protobuf-4.21.7-cp310-abi3-win_amd64.whl", hash = "sha256:3ec85328a35a16463c6f419dbce3c0fc42b3e904d966f17f48bae39597c7a543"}, - {file = "protobuf-4.21.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:db9056b6a11cb5131036d734bcbf91ef3ef9235d6b681b2fc431cbfe5a7f2e56"}, - {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ca200645d6235ce0df3ccfdff1567acbab35c4db222a97357806e015f85b5744"}, - {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b019c79e23a80735cc8a71b95f76a49a262f579d6b84fd20a0b82279f40e2cc1"}, - {file = "protobuf-4.21.7-cp37-cp37m-win32.whl", hash = "sha256:d3f89ccf7182293feba2de2739c8bf34fed1ed7c65a5cf987be00311acac57c1"}, - {file = "protobuf-4.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:a74d96cd960b87b4b712797c741bb3ea3a913f5c2dc4b6cbe9c0f8360b75297d"}, - {file = "protobuf-4.21.7-cp38-cp38-win32.whl", hash = "sha256:8e09d1916386eca1ef1353767b6efcebc0a6859ed7f73cb7fb974feba3184830"}, - {file = "protobuf-4.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:9e355f2a839d9930d83971b9f562395e13493f0e9211520f8913bd11efa53c02"}, - {file = "protobuf-4.21.7-cp39-cp39-win32.whl", hash = "sha256:f370c0a71712f8965023dd5b13277444d3cdfecc96b2c778b0e19acbfd60df6e"}, - {file = "protobuf-4.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:9643684232b6b340b5e63bb69c9b4904cdd39e4303d498d1a92abddc7e895b7f"}, - {file = "protobuf-4.21.7-py2.py3-none-any.whl", hash = "sha256:8066322588d4b499869bf9f665ebe448e793036b552f68c585a9b28f1e393f66"}, - {file = "protobuf-4.21.7-py3-none-any.whl", hash = "sha256:58b81358ec6c0b5d50df761460ae2db58405c063fd415e1101209221a0a810e1"}, - {file = "protobuf-4.21.7.tar.gz", hash = "sha256:71d9dba03ed3432c878a801e2ea51e034b0ea01cf3a4344fb60166cb5f6c8757"}, + {file = "protobuf-4.21.9-cp310-abi3-win32.whl", hash = "sha256:6e0be9f09bf9b6cf497b27425487706fa48c6d1632ddd94dab1a5fe11a422392"}, + {file = "protobuf-4.21.9-cp310-abi3-win_amd64.whl", hash = "sha256:a7d0ea43949d45b836234f4ebb5ba0b22e7432d065394b532cdca8f98415e3cf"}, + {file = "protobuf-4.21.9-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5ab0b8918c136345ff045d4b3d5f719b505b7c8af45092d7f45e304f55e50a1"}, + {file = "protobuf-4.21.9-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:2c9c2ed7466ad565f18668aa4731c535511c5d9a40c6da39524bccf43e441719"}, + {file = "protobuf-4.21.9-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:e575c57dc8b5b2b2caa436c16d44ef6981f2235eb7179bfc847557886376d740"}, + {file = "protobuf-4.21.9-cp37-cp37m-win32.whl", hash = "sha256:9227c14010acd9ae7702d6467b4625b6fe853175a6b150e539b21d2b2f2b409c"}, + {file = "protobuf-4.21.9-cp37-cp37m-win_amd64.whl", hash = "sha256:a419cc95fca8694804709b8c4f2326266d29659b126a93befe210f5bbc772536"}, + {file = "protobuf-4.21.9-cp38-cp38-win32.whl", hash = "sha256:5b0834e61fb38f34ba8840d7dcb2e5a2f03de0c714e0293b3963b79db26de8ce"}, + {file = "protobuf-4.21.9-cp38-cp38-win_amd64.whl", hash = "sha256:84ea107016244dfc1eecae7684f7ce13c788b9a644cd3fca5b77871366556444"}, + {file = "protobuf-4.21.9-cp39-cp39-win32.whl", hash = "sha256:f9eae277dd240ae19bb06ff4e2346e771252b0e619421965504bd1b1bba7c5fa"}, + {file = "protobuf-4.21.9-cp39-cp39-win_amd64.whl", hash = "sha256:6e312e280fbe3c74ea9e080d9e6080b636798b5e3939242298b591064470b06b"}, + {file = "protobuf-4.21.9-py2.py3-none-any.whl", hash = "sha256:7eb8f2cc41a34e9c956c256e3ac766cf4e1a4c9c925dc757a41a01be3e852965"}, + {file = "protobuf-4.21.9-py3-none-any.whl", hash = "sha256:48e2cd6b88c6ed3d5877a3ea40df79d08374088e89bedc32557348848dff250b"}, + {file = "protobuf-4.21.9.tar.gz", hash = "sha256:61f21493d96d2a77f9ca84fefa105872550ab5ef71d21c458eb80edcf4885a99"}, ] psycopg2-binary = [ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, @@ -1664,28 +1773,42 @@ pycryptodomex = [ {file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"}, ] pydantic = [ - {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, - {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, - {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, - {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, - {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, - {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, - {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, - {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, - {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, - {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, + {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, + {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, + {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, + {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, + {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, + {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, + {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, + {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, + {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, + {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, ] pyln-bolt7 = [ {file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"}, @@ -1730,9 +1853,13 @@ pytest-cov = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] +python-bitcoinlib = [ + {file = "python-bitcoinlib-0.11.2.tar.gz", hash = "sha256:61ba514e0d232cc84741e49862dcedaf37199b40bba252a17edc654f63d13f39"}, + {file = "python_bitcoinlib-0.11.2-py3-none-any.whl", hash = "sha256:78bd4ee717fe805cd760dfdd08765e77b7c7dbef4627f8596285e84953756508"}, +] python-dotenv = [ - {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"}, - {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"}, + {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"}, + {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"}, ] PyYAML = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -1769,6 +1896,10 @@ Represent = [ {file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"}, {file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"}, ] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] rfc3986 = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, @@ -1799,8 +1930,8 @@ secp256k1 = [ {file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"}, ] setuptools = [ - {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, - {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, + {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, + {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, ] shortuuid = [ {file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"}, @@ -1811,48 +1942,44 @@ six = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] sniffio = [ - {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, - {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] SQLAlchemy = [ - {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"}, - {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"}, - {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"}, - {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"}, - {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"}, - {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"}, - {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"}, - {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"}, + {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"}, + {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"}, + {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-win32.whl", hash = "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7"}, + {file = "SQLAlchemy-1.3.24-cp35-cp35m-win_amd64.whl", hash = "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-win32.whl", hash = "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996"}, + {file = "SQLAlchemy-1.3.24-cp36-cp36m-win_amd64.whl", hash = "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-win32.whl", hash = "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c"}, + {file = "SQLAlchemy-1.3.24-cp37-cp37m-win_amd64.whl", hash = "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-win32.whl", hash = "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba"}, + {file = "SQLAlchemy-1.3.24-cp38-cp38-win_amd64.whl", hash = "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-win32.whl", hash = "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064"}, + {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"}, + {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"}, ] sqlalchemy-aio = [ {file = "sqlalchemy_aio-0.17.0-py3-none-any.whl", hash = "sha256:3f4aa392c38f032d6734826a4138a0f02ed3122d442ed142be1e5964f2a33b60"}, @@ -1896,17 +2023,20 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] types-protobuf = [ - {file = "types-protobuf-3.20.4.tar.gz", hash = "sha256:0dad3a5009895c985a56e2837f61902bad9594151265ac0ee907bb16d0b01eb7"}, - {file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"}, + {file = "types-protobuf-3.20.4.6.tar.gz", hash = "sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965"}, + {file = "types_protobuf-3.20.4.6-py3-none-any.whl", hash = "sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, - {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, - {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] +urllib3 = [ + {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] uvicorn = [ - {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"}, - {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"}, + {file = "uvicorn-0.18.3-py3-none-any.whl", hash = "sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af"}, + {file = "uvicorn-0.18.3.tar.gz", hash = "sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b"}, ] uvloop = [ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"}, @@ -1966,6 +2096,6 @@ win32-setctime = [ {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, ] zipp = [ - {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, - {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, + {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, + {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, ] diff --git a/pyproject.toml b/pyproject.toml index e66073c5..5bd685b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,58 +12,60 @@ script = "build.py" python = "^3.10 | ^3.9 | ^3.8 | ^3.7" aiofiles = "0.8.0" asgiref = "3.4.1" -attrs = "21.2.0" +attrs = "22.1.0" bech32 = "1.2.0" bitstring = "3.1.9" -certifi = "2021.5.30" -charset-normalizer = "2.0.6" -click = "8.0.1" -ecdsa = "0.17.0" +certifi = "2022.9.24" +charset-normalizer = "2.0.12" +click = "8.0.4" +ecdsa = "0.18.0" embit = "0.4.9" -environs = "9.3.3" -fastapi = "0.78.0" +environs = "9.5.0" +fastapi = "0.83.0" h11 = "0.12.0" httpcore = "0.15.0" httptools = "0.4.0" httpx = "0.23.0" -idna = "3.2" -importlib-metadata = "4.8.1" +idna = "3.4" +importlib-metadata = "5.0.0" jinja2 = "3.0.1" lnurl = "0.3.6" markupsafe = "2.0.1" -marshmallow = "3.17.0" -outcome = "1.1.0" +marshmallow = "3.18.0" +outcome = "1.2.0" psycopg2-binary = "2.9.1" pycryptodomex = "3.14.1" -pydantic = "1.8.2" +pydantic = "1.10.2" pypng = "0.0.21" pyqrcode = "1.2.1" pyScss = "1.4.0" -python-dotenv = "0.19.0" +python-dotenv = "0.21.0" pyyaml = "5.4.1" represent = "1.6.0.post0" rfc3986 = "1.5.0" secp256k1 = "0.14.0" shortuuid = "1.0.1" six = "1.16.0" -sniffio = "1.2.0" -sqlalchemy = "1.3.23" +sniffio = "1.3.0" +sqlalchemy = "1.3.24" sqlalchemy-aio = "0.17.0" sse-starlette = "0.6.2" -typing-extensions = "3.10.0.2" -uvicorn = "0.18.1" +typing-extensions = "^4.4.0" +uvicorn = "0.18.3" uvloop = "0.16.0" watchgod = "0.7" websockets = "10.0" -zipp = "3.5.0" -loguru = "0.5.3" -cffi = "1.15.0" +zipp = "3.9.0" +loguru = "0.6.0" +cffi = "1.15.1" websocket-client = "1.3.3" grpcio = "^1.49.1" protobuf = "^4.21.6" Cerberus = "^1.3.4" async-timeout = "^4.0.2" pyln-client = "0.11.1" +cashu = "0.5.4" + [tool.poetry.dev-dependencies] isort = "^5.10.1" diff --git a/requirements.txt b/requirements.txt index 1431478c..06e8642f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,45 +1,49 @@ aiofiles==0.8.0 ; python_version >= "3.7" and python_version < "4.0" -anyio==3.6.1 ; python_version >= "3.7" and python_version < "4.0" +anyio==3.6.2 ; python_version >= "3.7" and python_version < "4.0" asgiref==3.4.1 ; python_version >= "3.7" and python_version < "4.0" asn1crypto==1.5.1 ; python_version >= "3.7" and python_version < "4.0" async-timeout==4.0.2 ; python_version >= "3.7" and python_version < "4.0" -attrs==21.2.0 ; python_version >= "3.7" and python_version < "4.0" +attrs==22.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" bitstring==3.1.9 ; python_version >= "3.7" and python_version < "4.0" +cashu==0.5.4 ; python_version >= "3.7" and python_version < "4.0" cerberus==1.3.4 ; python_version >= "3.7" and python_version < "4.0" -certifi==2021.5.30 ; python_version >= "3.7" and python_version < "4.0" -cffi==1.15.0 ; python_version >= "3.7" and python_version < "4.0" -charset-normalizer==2.0.6 ; python_version >= "3.7" and python_version < "4.0" -click==8.0.1 ; python_version >= "3.7" and python_version < "4.0" +certifi==2022.9.24 ; python_version >= "3.7" and python_version < "4.0" +cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0" +charset-normalizer==2.0.12 ; python_version >= "3.7" and python_version < "4.0" +click==8.0.4 ; python_version >= "3.7" and python_version < "4.0" coincurve==17.0.0 ; python_version >= "3.7" and python_version < "4.0" colorama==0.4.5 ; 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.17.0 ; python_version >= "3.7" and python_version < "4.0" +ecdsa==0.18.0 ; python_version >= "3.7" and python_version < "4.0" embit==0.4.9 ; python_version >= "3.7" and python_version < "4.0" enum34==1.1.10 ; python_version >= "3.7" and python_version < "4.0" -environs==9.3.3 ; python_version >= "3.7" and python_version < "4.0" -fastapi==0.78.0 ; python_version >= "3.7" and python_version < "4.0" -grpcio==1.49.1 ; python_version >= "3.7" and python_version < "4.0" +environs==9.5.0 ; python_version >= "3.7" and python_version < "4.0" +fastapi==0.83.0 ; python_version >= "3.7" and python_version < "4.0" +grpcio==1.50.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" httptools==0.4.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.2 ; python_version >= "3.7" and python_version < "4.0" -importlib-metadata==4.8.1 ; python_version >= "3.7" and python_version < "4.0" +idna==3.4 ; python_version >= "3.7" and python_version < "4.0" +importlib-metadata==5.0.0 ; python_version >= "3.7" and python_version < "4.0" +iniconfig==1.1.1 ; python_version >= "3.7" and python_version < "4.0" jinja2==3.0.1 ; python_version >= "3.7" and python_version < "4.0" lnurl==0.3.6 ; python_version >= "3.7" and python_version < "4.0" -loguru==0.5.3 ; python_version >= "3.7" and python_version < "4.0" +loguru==0.6.0 ; python_version >= "3.7" and python_version < "4.0" markupsafe==2.0.1 ; python_version >= "3.7" and python_version < "4.0" -marshmallow==3.17.0 ; python_version >= "3.7" and python_version < "4.0" -outcome==1.1.0 ; python_version >= "3.7" and python_version < "4.0" +marshmallow==3.18.0 ; python_version >= "3.7" and python_version < "4.0" +outcome==1.2.0 ; python_version >= "3.7" and python_version < "4.0" packaging==21.3 ; python_version >= "3.7" and python_version < "4.0" pathlib2==2.3.7.post1 ; python_version >= "3.7" and python_version < "4.0" -protobuf==4.21.7 ; python_version >= "3.7" and python_version < "4.0" +pluggy==1.0.0 ; python_version >= "3.7" and python_version < "4.0" +protobuf==4.21.9 ; python_version >= "3.7" and python_version < "4.0" psycopg2-binary==2.9.1 ; python_version >= "3.7" and python_version < "4.0" +py==1.11.0 ; python_version >= "3.7" and python_version < "4.0" pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0" pycryptodomex==3.14.1 ; python_version >= "3.7" and python_version < "4.0" -pydantic==1.8.2 ; python_version >= "3.7" and python_version < "4.0" +pydantic==1.10.2 ; python_version >= "3.7" and python_version < "4.0" pyln-bolt7==1.0.246 ; python_version >= "3.7" and python_version < "4.0" pyln-client==0.11.1 ; python_version >= "3.7" and python_version < "4.0" pyln-proto==0.11.1 ; python_version >= "3.7" and python_version < "4.0" @@ -48,25 +52,31 @@ pypng==0.0.21 ; python_version >= "3.7" and python_version < "4.0" pyqrcode==1.2.1 ; python_version >= "3.7" and python_version < "4.0" pyscss==1.4.0 ; python_version >= "3.7" and python_version < "4.0" pysocks==1.7.1 ; python_version >= "3.7" and python_version < "4.0" -python-dotenv==0.19.0 ; 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.1.3 ; 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.0 ; python_version >= "3.7" and python_version < "4.0" pyyaml==5.4.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.27.1 ; python_version >= "3.7" and python_version < "4.0" rfc3986==1.5.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.4.1 ; python_version >= "3.7" and python_version < "4.0" +setuptools==65.6.3 ; python_version >= "3.7" and python_version < "4.0" shortuuid==1.0.1 ; python_version >= "3.7" and python_version < "4.0" six==1.16.0 ; python_version >= "3.7" and python_version < "4.0" -sniffio==1.2.0 ; python_version >= "3.7" and python_version < "4.0" +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.23 ; python_version >= "3.7" and python_version < "4.0" +sqlalchemy==1.3.24 ; python_version >= "3.7" and python_version < "4.0" sse-starlette==0.6.2 ; python_version >= "3.7" and python_version < "4.0" starlette==0.19.1 ; python_version >= "3.7" and python_version < "4.0" -typing-extensions==3.10.0.2 ; python_version >= "3.7" and python_version < "4.0" -uvicorn==0.18.1 ; python_version >= "3.7" and python_version < "4.0" +tomli==2.0.1 ; python_version >= "3.7" and python_version < "4.0" +typing-extensions==4.4.0 ; python_version >= "3.7" and python_version < "4.0" +urllib3==1.26.12 ; python_version >= "3.7" and python_version < "4" +uvicorn==0.18.3 ; python_version >= "3.7" and python_version < "4.0" uvloop==0.16.0 ; python_version >= "3.7" and python_version < "4.0" watchgod==0.7 ; python_version >= "3.7" and python_version < "4.0" websocket-client==1.3.3 ; python_version >= "3.7" and python_version < "4.0" websockets==10.0 ; 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.5.0 ; python_version >= "3.7" and python_version < "4.0" +zipp==3.9.0 ; python_version >= "3.7" and python_version < "4.0" diff --git a/tests/data/mock_data.zip b/tests/data/mock_data.zip index 5a52941c74a0ef02265cb3ad046998b113cfa3a0..e992b988747cae0cb4472f77ab7f21d70787fb1b 100644 GIT binary patch literal 37478 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

$1NA1_wC}lP0O_RRlb3{?bB4$UQ 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 z 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