diff --git a/backend/app/exceptions.py b/backend/app/exceptions.py index a016d218b0..5c96b15b20 100644 --- a/backend/app/exceptions.py +++ b/backend/app/exceptions.py @@ -300,6 +300,14 @@ def __init__(self): super().__init__(self.description, self.code) +class DelegationCheckWrongParams(OctantException): + code = 400 + description = "Please specify at least 2 and not more than 10 addresses" + + def __init__(self): + super().__init__(self.description, self.code) + + class AntisybilScoreTooLow(OctantException): code = 400 description = "Antisybil score {} is lower then {}" diff --git a/backend/app/infrastructure/routes/delegation.py b/backend/app/infrastructure/routes/delegation.py index b05be11e7c..6e76369be4 100644 --- a/backend/app/infrastructure/routes/delegation.py +++ b/backend/app/infrastructure/routes/delegation.py @@ -34,6 +34,21 @@ ) +score_delegation_check_model = api.model( + "ScoreDelegationCheckResult", + { + "primary": fields.String( + required=False, + description="Address that receives delegated score", + ), + "secondary": fields.String( + required=False, + description="Address that donates delegated score", + ), + }, +) + + @ns.route("/delegate") @ns.doc( description="Delegates UQ score from secondary address to primary address", @@ -64,3 +79,18 @@ def put(self): controller.recalculate_uq_score(ns.payload) return {}, 204 + + +@ns.route("/check/") +@ns.doc( + description="Allows wallet to check if its accounts are delegating to each other. Implementation of this feature relies on a fact that Ethereum has > 250mil addresses, so blind enumeration is hard. We intend to replace it with proper zk-based delegation as soon as possible", + params={ + "addresses": "Ethereum addresses in hexadecimal format (case-insensitive, prefixed with 0x), separated by comma. At most 10 addresses at the same time" + }, +) +class UQScoreDelegationCheck(OctantResource): + @ns.marshal_with(score_delegation_check_model) + @ns.response(200, "User's delegations reconstructed") + def get(self, addresses: str): + secondary, primary = controller.delegation_check(addresses) + return {"primary": primary, "secondary": secondary}, 200 diff --git a/backend/app/modules/modules_factory/protocols.py b/backend/app/modules/modules_factory/protocols.py index dca7b2ac9d..c14e1d9aec 100644 --- a/backend/app/modules/modules_factory/protocols.py +++ b/backend/app/modules/modules_factory/protocols.py @@ -238,3 +238,6 @@ def delegate(self, context: Context, payload: ScoreDelegationPayload): def recalculate(self, context: Context, payload: ScoreDelegationPayload): ... + + def check(self, context: Context, addresses: list[str]) -> set[Tuple[str, str]]: + ... diff --git a/backend/app/modules/score_delegation/controller.py b/backend/app/modules/score_delegation/controller.py index bd9de17887..6355bd5d62 100644 --- a/backend/app/modules/score_delegation/controller.py +++ b/backend/app/modules/score_delegation/controller.py @@ -1,7 +1,10 @@ -from eth_utils import to_checksum_address +from typing import Tuple + +from eth_utils.address import to_checksum_address from app.context.epoch_state import EpochState from app.context.manager import state_context +from app.exceptions import DelegationCheckWrongParams, DelegationDoesNotExist from app.modules.dto import ScoreDelegationPayload from app.modules.modules_factory.current import CurrentServices from app.modules.registry import get_services @@ -21,6 +24,22 @@ def recalculate_uq_score(payload: dict): services.score_delegation_service.recalculate(context, score_delegation_payload) +def delegation_check(addresses: str) -> Tuple[str, str]: + tokens = addresses.split(",") + if len(tokens) < 2: + raise DelegationCheckWrongParams() + if len(tokens) > 10: + raise DelegationCheckWrongParams() + context = state_context(EpochState.CURRENT) + services: CurrentServices = get_services(EpochState.CURRENT) + pairs = list(services.score_delegation_service.check(context, tokens)) + if not pairs: + raise DelegationDoesNotExist() + if len(pairs) > 1: + raise DelegationDoesNotExist() + return pairs[0] + + def _deserialize_payload(payload: dict) -> ScoreDelegationPayload: return ScoreDelegationPayload( primary_addr=to_checksum_address(payload["primaryAddr"]), diff --git a/backend/app/modules/score_delegation/core.py b/backend/app/modules/score_delegation/core.py index 43c6bb9a94..fe6db2a467 100644 --- a/backend/app/modules/score_delegation/core.py +++ b/backend/app/modules/score_delegation/core.py @@ -1,6 +1,9 @@ -import hashlib -from collections import namedtuple +from itertools import permutations +from typing import NamedTuple from enum import Enum +from eth_utils.address import to_checksum_address +import hashlib +from typing import Tuple from app.exceptions import ( DelegationAlreadyExists, @@ -18,9 +21,11 @@ MIN_SCORE = 15 -HashedAddresses = namedtuple( - "HashedAddresses", ["primary_addr_hash", "secondary_addr_hash", "both_hash"] -) + +class HashedAddresses(NamedTuple): + primary_addr_hash: str + secondary_addr_hash: str + both_hash: str class ActionType(Enum): @@ -33,10 +38,18 @@ def build_score_delegation_message(primary_addr: str, secondary_addr: str) -> st def get_hashed_addresses( - payload: ScoreDelegationPayload, salt: str, salt_primary: str + payload: ScoreDelegationPayload, + salt: str, + salt_primary: str, + normalize: bool = True, ) -> HashedAddresses: - primary_addr_data = salt_primary + payload.primary_addr - secondary_addr_data = salt + payload.secondary_addr + primary = payload.primary_addr + secondary = payload.secondary_addr + if normalize: + primary = to_checksum_address(primary) + secondary = to_checksum_address(secondary) + primary_addr_data = salt_primary + primary + secondary_addr_data = salt + secondary hashed_primary = hashlib.sha256(primary_addr_data.encode()).hexdigest() hashed_secondary = hashlib.sha256(secondary_addr_data.encode()).hexdigest() @@ -47,6 +60,29 @@ def get_hashed_addresses( return HashedAddresses(hashed_primary, hashed_secondary, hashed_both) +def delegation_check( + addresses: list[str], + all_hashes: set[str], + salt: str, + salt_primary: str, + normalize=True, +) -> set[Tuple[str, str]]: + result = [] + for secondary, primary in permutations(addresses, 2): + payload = ScoreDelegationPayload( + primary_addr=primary, + secondary_addr=secondary, + primary_addr_signature=None, + secondary_addr_signature=None, + ) + _, _, both = get_hashed_addresses( + payload, salt, salt_primary, normalize=normalize + ) + if both in all_hashes: + result.append((secondary, primary)) + return set(result) + + def verify_score_delegation( hashed_addresses: HashedAddresses, all_hashes: set[str], diff --git a/backend/app/modules/score_delegation/service/simple_obfuscation.py b/backend/app/modules/score_delegation/service/simple_obfuscation.py index 461b49836a..455220a67e 100644 --- a/backend/app/modules/score_delegation/service/simple_obfuscation.py +++ b/backend/app/modules/score_delegation/service/simple_obfuscation.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Protocol, runtime_checkable +from typing import Protocol, runtime_checkable, Tuple, Container from app.context.manager import Context from app.extensions import db @@ -17,7 +17,7 @@ class Antisybil(Protocol): def fetch_antisybil_status( self, _: Context, user_address: str - ) -> (float, datetime, any): + ) -> Tuple[float, datetime, any]: ... def update_antisybil_status( @@ -66,6 +66,15 @@ def recalculate(self, context: Context, payload: ScoreDelegationPayload): self._delegation(context, payload, ActionType.RECALCULATION) db.session.commit() + def check(self, _: Context, addresses: list[str]) -> Container[Tuple[str, str]]: + all_hashes = database.score_delegation.get_all_delegations() + return core.delegation_check( + addresses, + all_hashes, + app.config["DELEGATION_SALT"], + app.config["DELEGATION_SALT_PRIMARY"], + ) + def _delegation( self, context: Context, payload: ScoreDelegationPayload, action: ActionType ): diff --git a/backend/poetry.lock b/backend/poetry.lock index e0502a52c0..25f64010aa 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -113,7 +112,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -128,7 +126,6 @@ frozenlist = ">=1.1.0" name = "alembic" version = "1.12.0" description = "A database migration tool for SQLAlchemy." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -148,7 +145,6 @@ tz = ["python-dateutil"] name = "aniso8601" version = "9.0.1" description = "A library for parsing ISO 8601 strings." -category = "main" optional = false python-versions = "*" files = [ @@ -163,7 +159,6 @@ dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] name = "annotated-types" version = "0.6.0" description = "Reusable constraint types to use with typing.Annotated" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -175,7 +170,6 @@ files = [ name = "apscheduler" version = "3.10.4" description = "In-process task scheduler with Cron-like capabilities" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -186,7 +180,7 @@ files = [ [package.dependencies] pytz = "*" six = ">=1.4.0" -tzlocal = ">=2.0,<3.0.0 || >=4.0.0" +tzlocal = ">=2.0,<3.dev0 || >=4.dev0" [package.extras] doc = ["sphinx", "sphinx-rtd-theme"] @@ -204,7 +198,6 @@ zookeeper = ["kazoo"] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -216,7 +209,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -235,7 +227,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -247,7 +238,6 @@ files = [ name = "bidict" version = "0.22.1" description = "The bidirectional mapping library for Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -264,7 +254,6 @@ test = ["hypothesis", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "py name = "bitarray" version = "2.8.1" description = "efficient arrays of booleans -- C extension" -category = "main" optional = false python-versions = "*" files = [ @@ -376,7 +365,6 @@ files = [ name = "black" version = "23.9.1" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -421,7 +409,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "blinker" version = "1.6.2" description = "Fast, simple object-to-object and broadcast signaling" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -433,7 +420,6 @@ files = [ name = "cachelib" version = "0.9.0" description = "A collection of cache libraries in the same API interface." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -445,7 +431,6 @@ files = [ name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -457,7 +442,6 @@ files = [ name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -542,7 +526,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -557,7 +540,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -569,7 +551,6 @@ files = [ name = "coverage" version = "7.3.1" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -634,7 +615,6 @@ toml = ["tomli"] name = "cytoolz" version = "0.12.2" description = "Cython implementation of Toolz: High performance functional utilities" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -743,7 +723,6 @@ cython = ["cython"] name = "dataclass-wizard" version = "0.22.2" description = "Marshal dataclasses to/from JSON. Use field properties with initial values. Construct a dataclass schema with JSON input." -category = "main" optional = false python-versions = "*" files = [ @@ -759,7 +738,6 @@ yaml = ["PyYAML (>=5.3)"] name = "dnspython" version = "2.4.2" description = "DNS toolkit" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -775,11 +753,23 @@ idna = ["idna (>=2.1,<4.0)"] trio = ["trio (>=0.14,<0.23)"] wmi = ["wmi (>=1.5.1,<2.0.0)"] +[[package]] +name = "epc" +version = "0.0.5" +description = "EPC (RPC stack for Emacs Lisp) implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "epc-0.0.5.tar.gz", hash = "sha256:a14d2ea74817955a20eb00812e3a4630a132897eb4d976420240f1152c0d7d25"}, +] + +[package.dependencies] +sexpdata = ">=0.0.3" + [[package]] name = "eth-abi" version = "4.2.1" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" -category = "main" optional = false python-versions = ">=3.7.2, <4" files = [ @@ -803,7 +793,6 @@ tools = ["hypothesis (>=4.18.2,<5.0.0)"] name = "eth-account" version = "0.9.0" description = "eth-account: Sign Ethereum transactions and messages with local private keys" -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -831,7 +820,6 @@ test = ["coverage", "hypothesis (>=4.18.0,<5)", "pytest (>=7.0.0)", "pytest-xdis name = "eth-hash" version = "0.5.2" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -854,7 +842,6 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] name = "eth-keyfile" version = "0.6.1" description = "A library for handling the encrypted keyfiles used to store ethereum private keys." -category = "main" optional = false python-versions = "*" files = [ @@ -877,7 +864,6 @@ test = ["pytest (>=6.2.5,<7)"] name = "eth-keys" version = "0.4.0" description = "Common API for Ethereum key operations." -category = "main" optional = false python-versions = "*" files = [ @@ -900,7 +886,6 @@ test = ["asn1tools (>=0.146.2,<0.147)", "eth-hash[pycryptodome]", "eth-hash[pysh name = "eth-rlp" version = "0.3.0" description = "eth-rlp: RLP definitions for common Ethereum objects in Python" -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -923,7 +908,6 @@ test = ["eth-hash[pycryptodome]", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox (= name = "eth-typing" version = "3.4.0" description = "eth-typing: Common type annotations for ethereum python packages" -category = "main" optional = false python-versions = ">=3.7.2, <4" files = [ @@ -941,7 +925,6 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] name = "eth-utils" version = "2.2.1" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" -category = "main" optional = false python-versions = ">=3.7,<4" files = [ @@ -965,7 +948,6 @@ test = ["hypothesis (>=4.43.0)", "mypy (==0.971)", "pytest (>=7.0.0)", "pytest-x name = "eventlet" version = "0.33.3" description = "Highly concurrent networking library" -category = "main" optional = false python-versions = "*" files = [ @@ -982,7 +964,6 @@ six = ">=1.10.0" name = "flake8" version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -999,7 +980,6 @@ pyflakes = ">=3.1.0,<3.2.0" name = "flake8-bugbear" version = "23.9.16" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -1018,7 +998,6 @@ dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", name = "flake8-pyproject" version = "1.2.3" description = "Flake8 plug-in loading the configuration from pyproject.toml" -category = "dev" optional = false python-versions = ">= 3.6" files = [ @@ -1035,7 +1014,6 @@ dev = ["pyTest", "pyTest-cov"] name = "flask" version = "2.3.3" description = "A simple framework for building complex web applications." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1058,7 +1036,6 @@ dotenv = ["python-dotenv"] name = "flask-apscheduler" version = "1.13.0" description = "Adds APScheduler support to Flask" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1074,7 +1051,6 @@ python-dateutil = ">=2.4.2" name = "flask-caching" version = "2.0.2" description = "Adds caching support to Flask applications." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1090,7 +1066,6 @@ Flask = "<3" name = "flask-cors" version = "4.0.0" description = "A Flask extension adding a decorator for CORS support" -category = "main" optional = false python-versions = "*" files = [ @@ -1105,7 +1080,6 @@ Flask = ">=0.9" name = "flask-migrate" version = "4.0.5" description = "SQLAlchemy database migrations for Flask applications using Alembic." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1122,7 +1096,6 @@ Flask-SQLAlchemy = ">=1.0" name = "flask-restx" version = "1.1.0" description = "Fully featured framework for fast, easy and documented API development with Flask" -category = "main" optional = false python-versions = "*" files = [ @@ -1146,7 +1119,6 @@ test = ["Faker (==2.0.0)", "blinker", "invoke (==2.0.0)", "mock (==3.0.5)", "pyt name = "flask-socketio" version = "5.3.6" description = "Socket.IO integration for Flask applications" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1165,7 +1137,6 @@ docs = ["sphinx"] name = "flask-sqlalchemy" version = "3.1.1" description = "Add SQLAlchemy support to your Flask application." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1181,7 +1152,6 @@ sqlalchemy = ">=2.0.16" name = "freezegun" version = "1.2.2" description = "Let your Python tests travel through time" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1196,7 +1166,6 @@ python-dateutil = ">=2.7" name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1267,7 +1236,6 @@ files = [ name = "gmpy2" version = "2.1.5" description = "gmpy2 interface to GMP/MPIR, MPFR, and MPC for Python 2.7 and 3.5+" -category = "main" optional = false python-versions = "*" files = [ @@ -1328,7 +1296,6 @@ files = [ name = "gql" version = "3.4.1" description = "GraphQL client for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1356,7 +1323,6 @@ websockets = ["websockets (>=10,<11)", "websockets (>=9,<10)"] name = "graphql-core" version = "3.2.3" description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -1368,7 +1334,6 @@ files = [ name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -1442,7 +1407,6 @@ test = ["objgraph", "psutil"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1454,7 +1418,6 @@ files = [ name = "hexbytes" version = "0.3.1" description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -1472,7 +1435,6 @@ test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>= name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1480,11 +1442,23 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importmagic" +version = "0.1.7" +description = "Python Import Magic - automagically add, remove and manage imports" +optional = false +python-versions = "*" +files = [ + {file = "importmagic-0.1.7.tar.gz", hash = "sha256:3f7757a5b74c9a291e20e12023bb3bf71bc2fa3adfb15a08570648ab83eaf8d8"}, +] + +[package.dependencies] +setuptools = ">=0.6b1" + [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1496,7 +1470,6 @@ files = [ name = "itsdangerous" version = "2.1.2" description = "Safely pass data to untrusted environments and back." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1508,7 +1481,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1526,7 +1498,6 @@ i18n = ["Babel (>=2.7)"] name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1546,7 +1517,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "lru-dict" version = "1.2.0" description = "An Dict like LRU container." -category = "main" optional = false python-versions = "*" files = [ @@ -1641,7 +1611,6 @@ test = ["pytest"] name = "mako" version = "1.2.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1661,7 +1630,6 @@ testing = ["pytest"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1721,7 +1689,6 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1733,7 +1700,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1817,7 +1783,6 @@ files = [ name = "multiproof" version = "0.1.2" description = "A Python library to generate merkle trees and merkle proofs." -category = "main" optional = false python-versions = "^3.10" files = [] @@ -1837,7 +1802,6 @@ resolved_reference = "e1f3633a10cb5929cc08d4f261effd170976e7b9" name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1845,11 +1809,21 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + [[package]] name = "numpy" version = "1.26.4" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -1895,7 +1869,6 @@ files = [ name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1907,7 +1880,6 @@ files = [ name = "pandas" version = "2.2.0" description = "Powerful data structures for data analysis, time series, and statistics" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -1979,7 +1951,6 @@ xml = ["lxml (>=4.9.2)"] name = "parsimonious" version = "0.9.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" -category = "main" optional = false python-versions = "*" files = [ @@ -1993,7 +1964,6 @@ regex = ">=2022.3.15" name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2005,7 +1975,6 @@ files = [ name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2021,7 +1990,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2037,7 +2005,6 @@ testing = ["pytest", "pytest-benchmark"] name = "protobuf" version = "4.24.3" description = "" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2060,7 +2027,6 @@ files = [ name = "psycopg2-binary" version = "2.9.7" description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2130,7 +2096,6 @@ files = [ name = "pycodestyle" version = "2.11.0" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2142,7 +2107,6 @@ files = [ name = "pycryptodome" version = "3.19.0" description = "Cryptographic library for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2184,7 +2148,6 @@ files = [ name = "pydantic" version = "2.6.0" description = "Data validation using Python type hints" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2204,7 +2167,6 @@ email = ["email-validator (>=2.0.0)"] name = "pydantic-core" version = "2.16.1" description = "" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2296,7 +2258,6 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" name = "pyflakes" version = "3.1.0" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2304,11 +2265,44 @@ files = [ {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] +[[package]] +name = "pylookup" +version = "0.2.2" +description = "PyLookup - Fuzzy-matching table autofill tool" +optional = false +python-versions = "*" +files = [ + {file = "PyLookup-0.2.2-py3-none-any.whl", hash = "sha256:a7153a5f25dbd999e1681a958dc55ba234b99793319f0fc92943f5c591575cbc"}, + {file = "PyLookup-0.2.2.tar.gz", hash = "sha256:252ab19790690f46689d9491c81817b26871f3a59cc8e9f13f02f18043bc496c"}, +] + +[package.dependencies] +click = "*" +pandas = "*" +rapidfuzz = "*" + +[[package]] +name = "pyright" +version = "1.1.366" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.366-py3-none-any.whl", hash = "sha256:c09e73ccc894976bcd6d6a5784aa84d724dbd9ceb7b873b39d475ca61c2de071"}, + {file = "pyright-1.1.366.tar.gz", hash = "sha256:10e4d60be411f6d960cd39b0b58bf2ff76f2c83b9aeb102ffa9d9fda2e1303cb"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" + +[package.extras] +all = ["twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] + [[package]] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2345,7 +2339,6 @@ files = [ name = "pytest" version = "7.4.2" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2366,7 +2359,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2385,7 +2377,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-mock" version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2403,7 +2394,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -2418,7 +2408,6 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2433,7 +2422,6 @@ cli = ["click (>=5.0)"] name = "python-engineio" version = "4.7.1" description = "Engine.IO server and client for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2453,7 +2441,6 @@ docs = ["sphinx"] name = "python-socketio" version = "5.9.0" description = "Socket.IO server and client for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2474,7 +2461,6 @@ docs = ["sphinx"] name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -2486,7 +2472,6 @@ files = [ name = "pyunormalize" version = "15.0.0" description = "Unicode normalization forms (NFC, NFKC, NFD, NFKD). A library independent from the Python core Unicode database." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2497,7 +2482,6 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" files = [ @@ -2517,11 +2501,115 @@ files = [ {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] +[[package]] +name = "rapidfuzz" +version = "3.9.3" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdb8c5b8e29238ec80727c2ba3b301efd45aa30c6a7001123a6647b8e6f77ea4"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3bd0d9632088c63a241f217742b1cf86e2e8ae573e01354775bd5016d12138c"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153f23c03d4917f6a1fc2fb56d279cc6537d1929237ff08ee7429d0e40464a18"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96c5225e840f1587f1bac8fa6f67562b38e095341576e82b728a82021f26d62"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b777cd910ceecd738adc58593d6ed42e73f60ad04ecdb4a841ae410b51c92e0e"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53e06e4b81f552da04940aa41fc556ba39dee5513d1861144300c36c33265b76"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c7ca5b6050f18fdcacdada2dc5fb7619ff998cd9aba82aed2414eee74ebe6cd"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:87bb8d84cb41446a808c4b5f746e29d8a53499381ed72f6c4e456fe0f81c80a8"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:959a15186d18425d19811bea86a8ffbe19fd48644004d29008e636631420a9b7"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a24603dd05fb4e3c09d636b881ce347e5f55f925a6b1b4115527308a323b9f8e"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d055da0e801c71dd74ba81d72d41b2fa32afa182b9fea6b4b199d2ce937450d"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:875b581afb29a7213cf9d98cb0f98df862f1020bce9d9b2e6199b60e78a41d14"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win32.whl", hash = "sha256:6073a46f61479a89802e3f04655267caa6c14eb8ac9d81a635a13805f735ebc1"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:119c010e20e561249b99ca2627f769fdc8305b07193f63dbc07bca0a6c27e892"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win_arm64.whl", hash = "sha256:790b0b244f3213581d42baa2fed8875f9ee2b2f9b91f94f100ec80d15b140ba9"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f57e8305c281e8c8bc720515540e0580355100c0a7a541105c6cafc5de71daae"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4fc7b784cf987dbddc300cef70e09a92ed1bce136f7bb723ea79d7e297fe76d"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b422c0a6fe139d5447a0766268e68e6a2a8c2611519f894b1f31f0a392b9167"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f50fed4a9b0c9825ff37cf0bccafd51ff5792090618f7846a7650f21f85579c9"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b80eb7cbe62348c61d3e67e17057cddfd6defab168863028146e07d5a8b24a89"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f45be77ec82da32ce5709a362e236ccf801615cc7163b136d1778cf9e31b14"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd84b7f652a5610733400307dc732f57c4a907080bef9520412e6d9b55bc9adc"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e6d27dad8c990218b8cd4a5c99cbc8834f82bb46ab965a7265d5aa69fc7ced7"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:05ee0696ebf0dfe8f7c17f364d70617616afc7dafe366532730ca34056065b8a"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2bc8391749e5022cd9e514ede5316f86e332ffd3cfceeabdc0b17b7e45198a8c"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93981895602cf5944d89d317ae3b1b4cc684d175a8ae2a80ce5b65615e72ddd0"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:754b719a4990735f66653c9e9261dcf52fd4d925597e43d6b9069afcae700d21"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win32.whl", hash = "sha256:14c9f268ade4c88cf77ab007ad0fdf63699af071ee69378de89fff7aa3cae134"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc1991b4cde6c9d3c0bbcb83d5581dc7621bec8c666c095c65b4277233265a82"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win_arm64.whl", hash = "sha256:0c34139df09a61b1b557ab65782ada971b4a3bce7081d1b2bee45b0a52231adb"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d6a210347d6e71234af5c76d55eeb0348b026c9bb98fe7c1cca89bac50fb734"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b300708c917ce52f6075bdc6e05b07c51a085733650f14b732c087dc26e0aaad"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83ea7ca577d76778250421de61fb55a719e45b841deb769351fc2b1740763050"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8319838fb5b7b5f088d12187d91d152b9386ce3979ed7660daa0ed1bff953791"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:505d99131afd21529293a9a7b91dfc661b7e889680b95534756134dc1cc2cd86"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c52970f7784518d7c82b07a62a26e345d2de8c2bd8ed4774e13342e4b3ff4200"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:143caf7247449055ecc3c1e874b69e42f403dfc049fc2f3d5f70e1daf21c1318"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b8ab0fa653d9225195a8ff924f992f4249c1e6fa0aea563f685e71b81b9fcccf"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57e7c5bf7b61c7320cfa5dde1e60e678d954ede9bb7da8e763959b2138391401"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:51fa1ba84653ab480a2e2044e2277bd7f0123d6693051729755addc0d015c44f"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:17ff7f7eecdb169f9236e3b872c96dbbaf116f7787f4d490abd34b0116e3e9c8"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afe7c72d3f917b066257f7ff48562e5d462d865a25fbcabf40fca303a9fa8d35"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win32.whl", hash = "sha256:e53ed2e9b32674ce96eed80b3b572db9fd87aae6742941fb8e4705e541d861ce"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:35b7286f177e4d8ba1e48b03612f928a3c4bdac78e5651379cec59f95d8651e6"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win_arm64.whl", hash = "sha256:e6e4b9380ed4758d0cb578b0d1970c3f32dd9e87119378729a5340cb3169f879"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a39890013f6d5b056cc4bfdedc093e322462ece1027a57ef0c636537bdde7531"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b5bc0fdbf419493163c5c9cb147c5fbe95b8e25844a74a8807dcb1a125e630cf"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe6e200a75a792d37b960457904c4fce7c928a96ae9e5d21d2bd382fe39066e"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de077c468c225d4c18f7188c47d955a16d65f21aab121cbdd98e3e2011002c37"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f917eaadf5388466a95f6a236f678a1588d231e52eda85374077101842e794e"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:858ba57c05afd720db8088a8707079e8d024afe4644001fe0dbd26ef7ca74a65"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d36447d21b05f90282a6f98c5a33771805f9222e5d0441d03eb8824e33e5bbb4"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:acbe4b6f1ccd5b90c29d428e849aa4242e51bb6cab0448d5f3c022eb9a25f7b1"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:53c7f27cdf899e94712972237bda48cfd427646aa6f5d939bf45d084780e4c16"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6175682a829c6dea4d35ed707f1dadc16513270ef64436568d03b81ccb6bdb74"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5276df395bd8497397197fca2b5c85f052d2e6a66ffc3eb0544dd9664d661f95"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:77b5c4f3e72924d7845f0e189c304270066d0f49635cf8a3938e122c437e58de"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-win32.whl", hash = "sha256:8add34061e5cd561c72ed4febb5c15969e7b25bda2bb5102d02afc3abc1f52d0"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:604e0502a39cf8e67fa9ad239394dddad4cdef6d7008fdb037553817d420e108"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21047f55d674614eb4b0ab34e35c3dc66f36403b9fbfae645199c4a19d4ed447"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a56da3aff97cb56fe85d9ca957d1f55dbac7c27da927a86a2a86d8a7e17f80aa"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:964c08481aec2fe574f0062e342924db2c6b321391aeb73d68853ed42420fd6d"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e2b827258beefbe5d3f958243caa5a44cf46187eff0c20e0b2ab62d1550327a"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6e65a301fcd19fbfbee3a514cc0014ff3f3b254b9fd65886e8a9d6957fb7bca"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe93ba1725a8d47d2b9dca6c1f435174859427fbc054d83de52aea5adc65729"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca21c0a34adee582775da997a600283e012a608a107398d80a42f9a57ad323d"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:256e07d3465173b2a91c35715a2277b1ee3ae0b9bbab4e519df6af78570741d0"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:802ca2cc8aa6b8b34c6fdafb9e32540c1ba05fca7ad60b3bbd7ec89ed1797a87"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:dd789100fc852cffac1449f82af0da139d36d84fd9faa4f79fc4140a88778343"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:5d0abbacdb06e27ff803d7ae0bd0624020096802758068ebdcab9bd49cf53115"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:378d1744828e27490a823fc6fe6ebfb98c15228d54826bf4e49e4b76eb5f5579"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win32.whl", hash = "sha256:5d0cb272d43e6d3c0dedefdcd9d00007471f77b52d2787a4695e9dd319bb39d2"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:15e4158ac4b3fb58108072ec35b8a69165f651ba1c8f43559a36d518dbf9fb3f"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win_arm64.whl", hash = "sha256:58c6a4936190c558d5626b79fc9e16497e5df7098589a7e80d8bff68148ff096"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5410dc848c947a603792f4f51b904a3331cf1dc60621586bfbe7a6de72da1091"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:282d55700a1a3d3a7980746eb2fcd48c9bbc1572ebe0840d0340d548a54d01fe"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc1037507810833646481f5729901a154523f98cbebb1157ba3a821012e16402"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e33f779391caedcba2ba3089fb6e8e557feab540e9149a5c3f7fea7a3a7df37"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41a81a9f311dc83d22661f9b1a1de983b201322df0c4554042ffffd0f2040c37"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a93250bd8fae996350c251e1752f2c03335bb8a0a5b0c7e910a593849121a435"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3617d1aa7716c57d120b6adc8f7c989f2d65bc2b0cbd5f9288f1fc7bf469da11"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad04a3f5384b82933213bba2459f6424decc2823df40098920856bdee5fd6e88"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8709918da8a88ad73c9d4dd0ecf24179a4f0ceba0bee21efc6ea21a8b5290349"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b770f85eab24034e6ef7df04b2bfd9a45048e24f8a808e903441aa5abde8ecdd"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930b4e6fdb4d914390141a2b99a6f77a52beacf1d06aa4e170cba3a98e24c1bc"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c8444e921bfc3757c475c4f4d7416a7aa69b2d992d5114fe55af21411187ab0d"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c1d3ef3878f871abe6826e386c3d61b5292ef5f7946fe646f4206b85836b5da"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d861bf326ee7dabc35c532a40384541578cd1ec1e1b7db9f9ecbba56eb76ca22"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cde6b9d9ba5007077ee321ec722fa714ebc0cbd9a32ccf0f4dd3cc3f20952d71"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb6546e7b6bed1aefbe24f68a5fb9b891cc5aef61bca6c1a7b1054b7f0359bb"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d8a57261ef7996d5ced7c8cba9189ada3fbeffd1815f70f635e4558d93766cb"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:67201c02efc596923ad950519e0b75ceb78d524177ea557134d6567b9ac2c283"}, + {file = "rapidfuzz-3.9.3.tar.gz", hash = "sha256:b398ea66e8ed50451bce5997c430197d5e4b06ac4aa74602717f792d8d8d06e2"}, +] + +[package.extras] +full = ["numpy"] + [[package]] name = "regex" version = "2023.8.8" description = "Alternative regular expression module, to replace re." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2619,7 +2707,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2641,7 +2728,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2656,7 +2742,6 @@ requests = ">=2.0.1,<3.0.0" name = "rlp" version = "3.0.0" description = "A package for Recursive Length Prefix encoding and decoding" -category = "main" optional = false python-versions = "*" files = [ @@ -2674,11 +2759,36 @@ lint = ["flake8 (==3.4.1)"] rust-backend = ["rusty-rlp (>=0.2.1,<0.3)"] test = ["hypothesis (==5.19.0)", "pytest (>=6.2.5,<7)", "tox (>=2.9.1,<3)"] +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "sexpdata" +version = "1.0.2" +description = "S-expression parser for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sexpdata-1.0.2-py3-none-any.whl", hash = "sha256:b39c918f055a85c5c35c1d4f7930aabb176bd29016e5ba5692e7e849914b2a1a"}, + {file = "sexpdata-1.0.2.tar.gz", hash = "sha256:92b67b0361f6766f8f9e44b9519cf3fbcfafa755db85bbf893c3e1cf4ddac109"}, +] + [[package]] name = "simple-websocket" version = "0.10.1" description = "Simple WebSocket server and client for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2693,7 +2803,6 @@ wsproto = "*" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -2705,7 +2814,6 @@ files = [ name = "sqlalchemy" version = "2.0.20" description = "Database Abstraction Library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2753,7 +2861,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""} typing-extensions = ">=4.2.0" [package.extras] @@ -2784,7 +2892,6 @@ sqlcipher = ["sqlcipher3-binary"] name = "toolz" version = "0.12.0" description = "List processing tools and functional utilities" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2796,7 +2903,6 @@ files = [ name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2808,7 +2914,6 @@ files = [ name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -2820,7 +2925,6 @@ files = [ name = "tzlocal" version = "5.0.1" description = "tzinfo object for the local timezone" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2838,7 +2942,6 @@ devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pyte name = "urllib3" version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2856,7 +2959,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "web3" version = "6.9.0" description = "web3.py" -category = "main" optional = false python-versions = ">=3.7.2" files = [ @@ -2892,7 +2994,6 @@ tester = ["eth-tester[py-evm] (==v0.9.1-b.1)", "py-geth (>=3.11.0)"] name = "websockets" version = "11.0.3" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2972,7 +3073,6 @@ files = [ name = "werkzeug" version = "2.3.7" description = "The comprehensive WSGI web application library." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2990,7 +3090,6 @@ watchdog = ["watchdog (>=2.3)"] name = "wsproto" version = "1.2.0" description = "WebSockets state-machine based protocol implementation" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3005,7 +3104,6 @@ h11 = ">=0.9.0,<1" name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3092,4 +3190,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "57cd56fc0a5e19828e85315ed9f3457911a79c3cb0e509655427f7f58c73aa2a" +content-hash = "d98b4637e3ae4d7190b7f607b0fcf756cf3054015aecbc70f3b9dfe01dbbf552" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index f8764a1008..b4ae1c5476 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -37,6 +37,10 @@ freezegun = "^1.2.2" flake8 = "^6.0.0" flake8-bugbear = "^23.7.10" flake8-pyproject = "^1.2.3" +pyright = "^1.1.366" +pylookup = "^0.2.2" +importmagic = "^0.1.7" +epc = "^0.0.5" [tool.poetry.group.prod] optional = true diff --git a/backend/tests/modules/score_delegation/test_score_delegation_core.py b/backend/tests/modules/score_delegation/test_score_delegation_core.py index c2aeed2bed..36cdacc196 100644 --- a/backend/tests/modules/score_delegation/test_score_delegation_core.py +++ b/backend/tests/modules/score_delegation/test_score_delegation_core.py @@ -1,11 +1,11 @@ import hashlib +from datetime import datetime, timedelta +from typing import Tuple import pytest from app import exceptions -from app.modules.dto import ( - ScoreDelegationPayload, -) +from app.modules.dto import ScoreDelegationPayload from app.modules.score_delegation import core from tests.helpers.constants import USER1_ADDRESS, USER2_ADDRESS, USER3_ADDRESS @@ -143,3 +143,54 @@ def test_score_is_sufficient(): core.verify_score_delegation( hashed_addresses, set(), 15.0, core.ActionType.DELEGATION ) + + +def _mk_db(delegations: list[Tuple[str, str]]) -> set[str]: + results = [] + for secondary, primary in delegations: + payload = ScoreDelegationPayload( + primary_addr=primary, + secondary_addr=secondary, + primary_addr_signature=None, + secondary_addr_signature=None, + ) + _, _, both = core.get_hashed_addresses(payload, "salt", "salt_primary") + results = results + [both] + return set(results) + + +def test_delegation_check(): + ALICE = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + BOB = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" + CAROL = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" + EVE = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" + FRANK = "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" + HEIDI = "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" + IVAN = "0x976EA74026E726554dB657fA54763abd0C3a0aa9" + JUDY = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" + MALLORY = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" + NICK = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" + + # no validity check of delegation is performed here + db_hashes = _mk_db([(BOB, ALICE), (EVE, CAROL), (ALICE, FRANK)]) + + def check(addresses): + return core.delegation_check(addresses, db_hashes, "salt", "salt_primary") + + assert set() == check([ALICE, EVE]) + assert set() == check([ALICE, ALICE]) + assert set() == check([CAROL, BOB]) + assert set() == check([CAROL, ALICE]) + assert set([(BOB, ALICE)]) == check([ALICE, BOB]) + assert set([(BOB, ALICE)]) == check([BOB, ALICE]) + assert set([(BOB, ALICE)]) == check([BOB, ALICE, EVE]) + assert set([(BOB, ALICE), (EVE, CAROL)]) == check([BOB, EVE, ALICE, CAROL]) + start = datetime.now() + assert set([(BOB, ALICE), (EVE, CAROL), (ALICE, FRANK)]) == check( + [ALICE, BOB, CAROL, EVE, FRANK, HEIDI, IVAN, JUDY, MALLORY, NICK] + ) + finish = datetime.now() + assert finish - start < timedelta(seconds=2) + + # check if address checksumming works as expected + assert set([(BOB, ALICE.lower())]) == check([ALICE.lower(), BOB])