From e91bfd685a0bf241b8dba65f3da479f595d10196 Mon Sep 17 00:00:00 2001 From: pacrob Date: Thu, 21 Dec 2023 09:45:32 -0700 Subject: [PATCH] lint and clean up typing, drop upper pins, match coincurve ci to proper python versions --- .bumpversion.cfg | 4 +- .circleci/config.yml | 100 ++++--- Makefile | 2 +- README.md | 196 ++++++-------- eth_keys/__init__.py | 20 +- eth_keys/backends/__init__.py | 22 +- eth_keys/backends/base.py | 34 +-- eth_keys/backends/coincurve.py | 64 ++--- eth_keys/backends/native/__init__.py | 4 +- eth_keys/backends/native/ecdsa.py | 71 ++--- eth_keys/backends/native/jacobian.py | 26 +- eth_keys/backends/native/main.py | 53 ++-- eth_keys/constants.py | 25 +- eth_keys/datatypes.py | 246 +++++++++--------- eth_keys/main.py | 49 ++-- eth_keys/tools/factories.py | 9 +- eth_keys/utils/der.py | 24 +- eth_keys/utils/module_loading.py | 28 +- eth_keys/utils/numeric.py | 3 +- eth_keys/utils/padding.py | 2 +- eth_keys/validation.py | 38 ++- mypy.ini | 15 -- pyproject.toml | 36 +-- pytest.ini | 2 - setup.py | 22 +- tests/backends/conftest.py | 88 ++++--- tests/backends/strategies.py | 18 +- tests/backends/test_backends.py | 91 ++++--- .../test_native_backend_against_coincurve.py | 110 ++++---- tests/conftest.py | 11 +- tests/core/test_factories.py | 4 +- .../test_key_and_signature_datastructures.py | 42 +-- tests/core/test_key_api_proxy_methods.py | 18 +- tests/core/test_keyapi_backend_formats.py | 16 +- tests/core/test_utils_der.py | 29 +-- tox.ini | 15 +- 36 files changed, 754 insertions(+), 783 deletions(-) delete mode 100644 mypy.ini delete mode 100644 pytest.ini diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6b94a15..95fb46f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,14 +3,14 @@ current_version = 0.4.0 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[^.]*)\.(?P\d+))? -serialize = +serialize = {major}.{minor}.{patch}-{stage}.{devnum} {major}.{minor}.{patch} [bumpversion:part:stage] optional_value = stable first_value = stable -values = +values = alpha beta stable diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c56313..35d67e9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -159,96 +159,93 @@ jobs: - image: cimg/python:3.8 environment: TOXENV: py38-backends-coincurve12 - py39-backends-coincurve12: + py38-backends-coincurve13: <<: *common docker: - - image: cimg/python:3.9 + - image: cimg/python:3.8 environment: - TOXENV: py39-backends-coincurve12 - py310-backends-coincurve12: + TOXENV: py38-backends-coincurve13 + py38-backends-coincurve14: <<: *common docker: - - image: cimg/python:3.10 + - image: cimg/python:3.8 environment: - TOXENV: py310-backends-coincurve12 - py311-backends-coincurve12: + TOXENV: py38-backends-coincurve14 + py38-backends-coincurve15: <<: *common docker: - - image: cimg/python:3.11 + - image: cimg/python:3.8 environment: - TOXENV: py311-backends-coincurve12 - py38-backends-coincurve13: + TOXENV: py38-backends-coincurve15 + py39-backends-coincurve15: <<: *common docker: - - image: cimg/python:3.8 + - image: cimg/python:3.9 environment: - TOXENV: py38-backends-coincurve13 - py39-backends-coincurve13: + TOXENV: py39-backends-coincurve15 + + py38-backends-coincurve16: <<: *common docker: - - image: cimg/python:3.9 + - image: cimg/python:3.8 environment: - TOXENV: py39-backends-coincurve13 - py310-backends-coincurve13: + TOXENV: py38-backends-coincurve16 + py39-backends-coincurve16: <<: *common docker: - - image: cimg/python:3.10 + - image: cimg/python:3.9 environment: - TOXENV: py310-backends-coincurve13 - py311-backends-coincurve13: + TOXENV: py39-backends-coincurve16 + py310-backends-coincurve16: <<: *common docker: - - image: cimg/python:3.11 + - image: cimg/python:3.10 environment: - TOXENV: py311-backends-coincurve13 - py38-backends-coincurve14: + TOXENV: py310-backends-coincurve16 + + py38-backends-coincurve17: <<: *common docker: - image: cimg/python:3.8 environment: - TOXENV: py38-backends-coincurve14 - py39-backends-coincurve14: + TOXENV: py38-backends-coincurve17 + py39-backends-coincurve17: <<: *common docker: - image: cimg/python:3.9 environment: - TOXENV: py39-backends-coincurve14 - py310-backends-coincurve14: + TOXENV: py39-backends-coincurve17 + py310-backends-coincurve17: <<: *common docker: - image: cimg/python:3.10 environment: - TOXENV: py310-backends-coincurve14 - py311-backends-coincurve14: - <<: *common - docker: - - image: cimg/python:3.11 - environment: - TOXENV: py311-backends-coincurve14 - py38-backends-coincurve15: + TOXENV: py310-backends-coincurve17 + + py38-backends-coincurve18: <<: *common docker: - image: cimg/python:3.8 environment: - TOXENV: py38-backends-coincurve15 - py39-backends-coincurve15: + TOXENV: py38-backends-coincurve18 + py39-backends-coincurve18: <<: *common docker: - image: cimg/python:3.9 environment: - TOXENV: py39-backends-coincurve15 - py310-backends-coincurve15: + TOXENV: py39-backends-coincurve18 + py310-backends-coincurve18: <<: *common docker: - image: cimg/python:3.10 environment: - TOXENV: py310-backends-coincurve15 - py311-backends-coincurve15: + TOXENV: py310-backends-coincurve18 + py311-backends-coincurve18: <<: *common docker: - image: cimg/python:3.11 environment: - TOXENV: py311-backends-coincurve15 + TOXENV: py311-backends-coincurve18 workflows: version: 2 @@ -269,18 +266,17 @@ workflows: - py311-wheel - py311-wheel-windows - py38-backends-coincurve12 - - py39-backends-coincurve12 - - py310-backends-coincurve12 - - py311-backends-coincurve12 - py38-backends-coincurve13 - - py39-backends-coincurve13 - - py310-backends-coincurve13 - - py311-backends-coincurve13 - py38-backends-coincurve14 - - py39-backends-coincurve14 - - py310-backends-coincurve14 - - py311-backends-coincurve14 - py38-backends-coincurve15 - py39-backends-coincurve15 - - py310-backends-coincurve15 - - py311-backends-coincurve15 + - py38-backends-coincurve16 + - py39-backends-coincurve16 + - py310-backends-coincurve16 + - py38-backends-coincurve17 + - py39-backends-coincurve17 + - py310-backends-coincurve17 + - py38-backends-coincurve18 + - py39-backends-coincurve18 + - py310-backends-coincurve18 + - py311-backends-coincurve18 diff --git a/Makefile b/Makefile index 2490edb..48faed9 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ lint: test: pytest tests -docs: check-docs +docs: python ./newsfragments/validate_files.py towncrier build --draft --version preview diff --git a/README.md b/README.md index 5bd1211..7e41ff2 100644 --- a/README.md +++ b/README.md @@ -17,61 +17,6 @@ Read more in the documentation below. [View the change log](https://github.com/e python -m pip install eth-keys ``` -## Developer Setup - -If you would like to hack on eth-keys, please check out the [Snake Charmers -Tactical Manual](https://github.com/ethereum/snake-charmers-tactical-manual) -for information on how we do: - -- Testing -- Pull Requests -- Documentation - -We use [pre-commit](https://pre-commit.com/) to maintain consistent code style. Once -installed, it will run automatically with every commit. You can also run it manually -with `make lint`. If you need to make a commit that skips the `pre-commit` checks, you -can do so with `git commit --no-verify`. - -### Development Environment Setup - -You can set up your dev environment with: - -```sh -git clone git@github.com:ethereum/eth-keys.git -cd eth-keys -virtualenv -p python3 venv -. venv/bin/activate -python -m pip install -e ".[dev]" -pre-commit install -``` - -### Release setup - -To release a new version: - -```sh -make release bump=$$VERSION_PART_TO_BUMP$$ -``` - -#### How to bumpversion - -The version format for this repo is `{major}.{minor}.{patch}` for stable, and -`{major}.{minor}.{patch}-{stage}.{devnum}` for unstable (`stage` can be alpha or beta). - -To issue the next version in line, specify which part to bump, -like `make release bump=minor` or `make release bump=devnum`. This is typically done from the -main branch, except when releasing a beta (in which case the beta is released from main, -and the previous stable branch is released from said branch). - -If you are in a beta version, `make release bump=stage` will switch to a stable. - -To issue an unstable version when the current version is stable, specify the -new version explicitly, like `make release bump="--new-version 4.0.0-alpha.1 devnum"` - - - -## QuickStart - ```python >>> from eth_keys import keys >>> pk = keys.PrivateKey(b'\x01' * 32) @@ -90,7 +35,6 @@ True True ``` - ## Documentation ### `KeyAPI(backend=None)` @@ -100,8 +44,8 @@ libary. The object takes a single optional argument in its constructor which designates what backend will be used for eliptical curve cryptography operations. The built-in backends are: -* `eth_keys.backends.NativeECCBackend`: A pure python implementation of the ECC operations. -* `eth_keys.backends.CoinCurveECCBackend`: Uses the [`coincurve`](https://github.com/ofek/coincurve) library for ECC operations. +- `eth_keys.backends.NativeECCBackend`: A pure python implementation of the ECC operations. +- `eth_keys.backends.CoinCurveECCBackend`: Uses the [`coincurve`](https://github.com/ofek/coincurve) library for ECC operations. By default, `eth-keys` will *try* to use the `CoinCurveECCBackend`, falling back to the `NativeECCBackend` if the `coincurve` library is not @@ -111,9 +55,9 @@ available. The `backend` argument can be given in any of the following forms. -* Instance of the backend class -* The backend class -* String with the dot-separated import path for the backend class. +- Instance of the backend class +- The backend class +- String with the dot-separated import path for the backend class. ```python >>> from eth_keys import KeyAPI @@ -135,56 +79,50 @@ to the desired backend. >>> os.environ['ECC_BACKEND_CLASS'] = 'eth_keys.backends.CoinCurveECCBackend' ``` - ### `KeyAPI.ecdsa_sign(message_hash, private_key) -> Signature` This method returns a signature for the given `message_hash`, signed by the provided `private_key`. -* `message_hash`: **must** be a byte string of length 32 -* `private_key`: **must** be an instance of `PrivateKey` - +- `message_hash`: **must** be a byte string of length 32 +- `private_key`: **must** be an instance of `PrivateKey` ### `KeyAPI.ecdsa_verify(message_hash, signature, public_key) -> bool` Returns `True` or `False` based on whether the provided `signature` is a valid signature for the provided `message_hash` and `public_key`. -* `message_hash`: **must** be a byte string of length 32 -* `signature`: **must** be an instance of `Signature` -* `public_key`: **must** be an instance of `PublicKey` - +- `message_hash`: **must** be a byte string of length 32 +- `signature`: **must** be an instance of `Signature` +- `public_key`: **must** be an instance of `PublicKey` ### `KeyAPI.ecdsa_recover(message_hash, signature) -> PublicKey` Returns the `PublicKey` instances recovered from the given `signature` and `message_hash`. -* `message_hash`: **must** be a byte string of length 32 -* `signature`: **must** be an instance of `Signature` - +- `message_hash`: **must** be a byte string of length 32 +- `signature`: **must** be an instance of `Signature` ### `KeyAPI.private_key_to_public_key(private_key) -> PublicKey` Returns the `PublicKey` instances computed from the given `private_key` instance. -* `private_key`: **must** be an instance of `PublicKey` - +- `private_key`: **must** be an instance of `PublicKey` ### Common APIs for `PublicKey`, `PrivateKey` and `Signature` There is a common API for the following objects. -* `PublicKey` -* `PrivateKey` -* `Signature` +- `PublicKey` +- `PrivateKey` +- `Signature` Each of these objects has all of the following APIs. -* `obj.to_bytes()`: Returns the object in it's canonical `bytes` serialization. -* `obj.to_hex()`: Returns a text string of the hex encoded canonical representation. - +- `obj.to_bytes()`: Returns the object in it's canonical `bytes` serialization. +- `obj.to_hex()`: Returns a text string of the hex encoded canonical representation. ### `KeyAPI.PublicKey(public_key_bytes)` @@ -196,101 +134,86 @@ The `PublicKey` class takes a single argument which must be a bytes string with The following methods are available: - #### `PublicKey.from_compressed_bytes(compressed_bytes) -> PublicKey` This `classmethod` returns a new `PublicKey` instance computed from its compressed representation. -* `compressed_bytes` **must** be a byte string of length 33 starting with `\x02` or `\x03`. - +- `compressed_bytes` **must** be a byte string of length 33 starting with `\x02` or `\x03`. #### `PublicKey.from_private(private_key) -> PublicKey` This `classmethod` returns a new `PublicKey` instance computed from the -given `private_key`. - -* `private_key` may either be a byte string of length 32 or an instance of the `KeyAPI.PrivateKey` class. +given `private_key`. +- `private_key` may either be a byte string of length 32 or an instance of the `KeyAPI.PrivateKey` class. #### `PublicKey.recover_from_msg(message, signature) -> PublicKey` This `classmethod` returns a new `PublicKey` instance computed from the provided `message` and `signature`. -* `message` **must** be a byte string -* `signature` **must** be an instance of `KeyAPI.Signature` - +- `message` **must** be a byte string +- `signature` **must** be an instance of `KeyAPI.Signature` #### `PublicKey.recover_from_msg_hash(message_hash, signature) -> PublicKey` Same as `PublicKey.recover_from_msg` except that `message_hash` should be the Keccak hash of the `message`. - #### `PublicKey.verify_msg(message, signature) -> bool` This method returns `True` or `False` based on whether the signature is a valid for the given message. - #### `PublicKey.verify_msg_hash(message_hash, signature) -> bool` Same as `PublicKey.verify_msg` except that `message_hash` should be the Keccak hash of the `message`. - #### `PublicKey.to_compressed_bytes() -> bytes` Returns the compressed representation of this public key. - #### `PublicKey.to_address() -> text` Returns the hex encoded ethereum address for this public key. - #### `PublicKey.to_checksum_address() -> text` Returns the ERC55 checksum formatted ethereum address for this public key. - #### `PublicKey.to_canonical_address() -> bytes` Returns the 20-byte representation of the ethereum address for this public key. - ### `KeyAPI.PrivateKey(private_key_bytes)` The `PrivateKey` class takes a single argument which must be a bytes string with length 32. The following methods and properties are available - #### `PrivateKey.public_key` This *property* holds the `PublicKey` instance coresponding to this private key. - #### `PrivateKey.sign_msg(message) -> Signature` This method returns a signature for the given `message` in the form of a `Signature` instance -* `message` **must** be a byte string. - +- `message` **must** be a byte string. #### `PrivateKey.sign_msg_hash(message_hash) -> Signature` Same as `PrivateKey.sign` except that `message_hash` should be the Keccak hash of the `message`. - ### `KeyAPI.Signature(signature_bytes=None, vrs=None)` The `Signature` class can be instantiated in one of two ways. -* `signature_bytes`: a bytes string with length 65. -* `vrs`: a 3-tuple composed of the integers `v`, `r`, and `s`. +- `signature_bytes`: a bytes string with length 65. +- `vrs`: a 3-tuple composed of the integers `v`, `r`, and `s`. > Note: If using the `signature_bytes` to instantiate, the byte string should be encoded as `r_bytes | s_bytes | v_bytes` where `|` represents concatenation. `r_bytes` and `s_bytes` should be 32 bytes in length. `v_bytes` should be a single byte `\x00` or `\x01`. @@ -298,55 +221,46 @@ Signatures are expected to use `1` or `0` for their `v` value. The following methods and properties are available - #### `Signature.v` This property returns the `v` value from the signature as an integer. - #### `Signature.r` This property returns the `r` value from the signature as an integer. - #### `Signature.s` This property returns the `s` value from the signature as an integer. - #### `Signature.vrs` This property returns a 3-tuple of `(v, r, s)`. - #### `Signature.verify_msg(message, public_key) -> bool` This method returns `True` or `False` based on whether the signature is a valid for the given public key. -* `message`: **must** be a byte string. -* `public_key`: **must** be an instance of `PublicKey` - +- `message`: **must** be a byte string. +- `public_key`: **must** be an instance of `PublicKey` #### `Signature.verify_msg_hash(message_hash, public_key) -> bool` Same as `Signature.verify_msg` except that `message_hash` should be the Keccak hash of the `message`. - #### `Signature.recover_public_key_from_msg(message) -> PublicKey` This method returns a `PublicKey` instance recovered from the signature. -* `message`: **must** be a byte string. - +- `message`: **must** be a byte string. #### `Signature.recover_public_key_from_msg_hash(message_hash) -> PublicKey` Same as `Signature.recover_public_key_from_msg` except that `message_hash` should be the Keccak hash of the `message`. - ### Exceptions #### `eth_api.exceptions.ValidationError` @@ -355,8 +269,58 @@ This error is raised during instantaition of any of the `PublicKey`, `PrivateKey` or `Signature` classes if their constructor parameters are invalid. - #### `eth_api.exceptions.BadSignature` This error is raised from any of the `recover` or `verify` methods involving signatures if the signature is invalid. + +## Developer Setup + +If you would like to hack on eth-keys, please check out the [Snake Charmers +Tactical Manual](https://github.com/ethereum/snake-charmers-tactical-manual) +for information on how we do: + +- Testing +- Pull Requests +- Documentation + +We use [pre-commit](https://pre-commit.com/) to maintain consistent code style. Once +installed, it will run automatically with every commit. You can also run it manually +with `make lint`. If you need to make a commit that skips the `pre-commit` checks, you +can do so with `git commit --no-verify`. + +### Development Environment Setup + +You can set up your dev environment with: + +```sh +git clone git@github.com:ethereum/eth-keys.git +cd eth-keys +virtualenv -p python3 venv +. venv/bin/activate +python -m pip install -e ".[dev]" +pre-commit install +``` + +### Release setup + +To release a new version: + +```sh +make release bump=$$VERSION_PART_TO_BUMP$$ +``` + +#### How to bumpversion + +The version format for this repo is `{major}.{minor}.{patch}` for stable, and +`{major}.{minor}.{patch}-{stage}.{devnum}` for unstable (`stage` can be alpha or beta). + +To issue the next version in line, specify which part to bump, +like `make release bump=minor` or `make release bump=devnum`. This is typically done from the +main branch, except when releasing a beta (in which case the beta is released from main, +and the previous stable branch is released from said branch). + +If you are in a beta version, `make release bump=stage` will switch to a stable. + +To issue an unstable version when the current version is stable, specify the +new version explicitly, like `make release bump="--new-version 4.0.0-alpha.1 devnum"` diff --git a/eth_keys/__init__.py b/eth_keys/__init__.py index e02dc18..8601480 100644 --- a/eth_keys/__init__.py +++ b/eth_keys/__init__.py @@ -1,18 +1,10 @@ -from __future__ import absolute_import - -import sys -import warnings - - -if sys.version_info.major < 3: - warnings.simplefilter('always', DeprecationWarning) - warnings.warn(DeprecationWarning( - "The `eth-keys` library is dropping support for Python 2. Upgrade to Python 3." - )) - warnings.resetwarnings() - +from importlib.metadata import ( + version as __version, +) -from .main import ( # noqa: F401 +from .main import ( KeyAPI, lazy_key_api as keys, ) + +__version__ = __version("eth-keys") diff --git a/eth_keys/backends/__init__.py b/eth_keys/backends/__init__.py index aaa3683..d19af45 100644 --- a/eth_keys/backends/__init__.py +++ b/eth_keys/backends/__init__.py @@ -1,31 +1,35 @@ -from __future__ import absolute_import - import os -from typing import Type +from typing import ( + Type, +) from eth_keys.utils.module_loading import ( import_string, ) -from .base import BaseECCBackend # noqa: F401 -from .coincurve import ( # noqa: F401 +from .base import ( + BaseECCBackend, +) +from .coincurve import ( CoinCurveECCBackend, is_coincurve_available, ) -from .native import NativeECCBackend # noqa: F401 +from .native import ( + NativeECCBackend, +) def get_default_backend_class() -> str: if is_coincurve_available(): - return 'eth_keys.backends.CoinCurveECCBackend' + return "eth_keys.backends.CoinCurveECCBackend" else: - return 'eth_keys.backends.NativeECCBackend' + return "eth_keys.backends.NativeECCBackend" def get_backend_class(import_path: str = None) -> Type[BaseECCBackend]: if import_path is None: import_path = os.environ.get( - 'ECC_BACKEND_CLASS', + "ECC_BACKEND_CLASS", get_default_backend_class(), ) return import_string(import_path) diff --git a/eth_keys/backends/base.py b/eth_keys/backends/base.py index 8991f95..0b6356d 100644 --- a/eth_keys/backends/base.py +++ b/eth_keys/backends/base.py @@ -1,5 +1,3 @@ -from typing import Any # noqa: F401 - from eth_keys.datatypes import ( BaseSignature, NonRecoverableSignature, @@ -9,36 +7,28 @@ ) -class BaseECCBackend(object): - def ecdsa_sign(self, - msg_hash: bytes, - private_key: PrivateKey) -> Signature: +class BaseECCBackend: + def ecdsa_sign(self, msg_hash: bytes, private_key: PrivateKey) -> Signature: raise NotImplementedError() - def ecdsa_sign_non_recoverable(self, - msg_hash: bytes, - private_key: PrivateKey) -> NonRecoverableSignature: + def ecdsa_sign_non_recoverable( + self, msg_hash: bytes, private_key: PrivateKey + ) -> NonRecoverableSignature: raise NotImplementedError() - def ecdsa_verify(self, - msg_hash: bytes, - signature: BaseSignature, - public_key: PublicKey) -> bool: + def ecdsa_verify( + self, msg_hash: bytes, signature: BaseSignature, public_key: PublicKey + ) -> bool: raise NotImplementedError() - def ecdsa_recover(self, - msg_hash: bytes, - signature: Signature) -> PublicKey: + def ecdsa_recover(self, msg_hash: bytes, signature: Signature) -> PublicKey: raise NotImplementedError() - def private_key_to_public_key(self, - private_key: PrivateKey) -> PublicKey: + def private_key_to_public_key(self, private_key: PrivateKey) -> PublicKey: raise NotImplementedError() - def decompress_public_key_bytes(self, - compressed_public_key_bytes: bytes) -> bytes: + def decompress_public_key_bytes(self, compressed_public_key_bytes: bytes) -> bytes: raise NotImplementedError() - def compress_public_key_bytes(self, - uncompressed_public_key_bytes: bytes) -> bytes: + def compress_public_key_bytes(self, uncompressed_public_key_bytes: bytes) -> bytes: raise NotImplementedError() diff --git a/eth_keys/backends/coincurve.py b/eth_keys/backends/coincurve.py index 8b65968..d6d5e51 100644 --- a/eth_keys/backends/coincurve.py +++ b/eth_keys/backends/coincurve.py @@ -1,12 +1,8 @@ -from __future__ import absolute_import - -from typing import Optional # noqa: F401 - from eth_utils import ( big_endian_to_int, ) -from eth_keys.datatypes import ( # noqa: F401 +from eth_keys.datatypes import ( BaseSignature, NonRecoverableSignature, PrivateKey, @@ -16,17 +12,19 @@ from eth_keys.exceptions import ( BadSignature, ) -from eth_keys.validation import ( - validate_uncompressed_public_key_bytes, -) from eth_keys.utils import ( der, ) from eth_keys.utils.numeric import ( coerce_low_s, ) +from eth_keys.validation import ( + validate_uncompressed_public_key_bytes, +) -from .base import BaseECCBackend +from .base import ( + BaseECCBackend, +) def is_coincurve_available() -> bool: @@ -43,15 +41,15 @@ def __init__(self) -> None: try: import coincurve except ImportError: - raise ImportError("The CoinCurveECCBackend requires the coincurve \ - library which is not available for import.") + raise ImportError( + "The CoinCurveECCBackend requires the coincurve " + "library which is not available for import." + ) self.keys = coincurve.keys self.ecdsa = coincurve.ecdsa - super(CoinCurveECCBackend, self).__init__() + super().__init__() - def ecdsa_sign(self, - msg_hash: bytes, - private_key: PrivateKey) -> Signature: + def ecdsa_sign(self, msg_hash: bytes, private_key: PrivateKey) -> Signature: private_key_bytes = private_key.to_bytes() signature_bytes = self.keys.PrivateKey(private_key_bytes).sign_recoverable( msg_hash, @@ -60,9 +58,9 @@ def ecdsa_sign(self, signature = Signature(signature_bytes, backend=self) return signature - def ecdsa_sign_non_recoverable(self, - msg_hash: bytes, - private_key: PrivateKey) -> NonRecoverableSignature: + def ecdsa_sign_non_recoverable( + self, msg_hash: bytes, private_key: PrivateKey + ) -> NonRecoverableSignature: private_key_bytes = private_key.to_bytes() der_encoded_signature = self.keys.PrivateKey(private_key_bytes).sign( @@ -74,11 +72,11 @@ def ecdsa_sign_non_recoverable(self, signature = NonRecoverableSignature(rs=rs, backend=self) return signature - def ecdsa_verify(self, - msg_hash: bytes, - signature: BaseSignature, - public_key: PublicKey) -> bool: - # coincurve rejects signatures with a high s, so convert to the equivalent low s form + def ecdsa_verify( + self, msg_hash: bytes, signature: BaseSignature, public_key: PublicKey + ) -> bool: + # coincurve rejects signatures with a high s, + # so convert to the equivalent low s form low_s = coerce_low_s(signature.s) der_encoded_signature = der.two_int_sequence_encoder(signature.r, low_s) coincurve_public_key = self.keys.PublicKey(b"\x04" + public_key.to_bytes()) @@ -88,9 +86,7 @@ def ecdsa_verify(self, hasher=None, ) - def ecdsa_recover(self, - msg_hash: bytes, - signature: Signature) -> PublicKey: + def ecdsa_recover(self, msg_hash: bytes, signature: Signature) -> PublicKey: signature_bytes = signature.to_bytes() try: public_key_bytes = self.keys.PublicKey.from_signature_and_message( @@ -98,26 +94,22 @@ def ecdsa_recover(self, msg_hash, hasher=None, ).format(compressed=False)[1:] - except (ValueError, Exception) as err: - # `coincurve` can raise `ValueError` or `Exception` dependending on - # how the signature is invalid. + except Exception as err: raise BadSignature(str(err)) public_key = PublicKey(public_key_bytes, backend=self) return public_key def private_key_to_public_key(self, private_key: PrivateKey) -> PublicKey: - public_key_bytes = self.keys.PrivateKey(private_key.to_bytes()).public_key.format( - compressed=False, - )[1:] + public_key_bytes = self.keys.PrivateKey( + private_key.to_bytes() + ).public_key.format(compressed=False,)[1:] return PublicKey(public_key_bytes, backend=self) - def decompress_public_key_bytes(self, - compressed_public_key_bytes: bytes) -> bytes: + def decompress_public_key_bytes(self, compressed_public_key_bytes: bytes) -> bytes: public_key = self.keys.PublicKey(compressed_public_key_bytes) return public_key.format(compressed=False)[1:] - def compress_public_key_bytes(self, - uncompressed_public_key_bytes: bytes) -> bytes: + def compress_public_key_bytes(self, uncompressed_public_key_bytes: bytes) -> bytes: validate_uncompressed_public_key_bytes(uncompressed_public_key_bytes) point = ( big_endian_to_int(uncompressed_public_key_bytes[:32]), diff --git a/eth_keys/backends/native/__init__.py b/eth_keys/backends/native/__init__.py index d7a2a73..b9e24f8 100644 --- a/eth_keys/backends/native/__init__.py +++ b/eth_keys/backends/native/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - -from .main import ( # noqa: F401 +from .main import ( NativeECCBackend, ) diff --git a/eth_keys/backends/native/ecdsa.py b/eth_keys/backends/native/ecdsa.py index 6719b7f..72068f3 100644 --- a/eth_keys/backends/native/ecdsa.py +++ b/eth_keys/backends/native/ecdsa.py @@ -3,36 +3,41 @@ """ import hashlib import hmac -from typing import (Any, Callable, Optional, Tuple) # noqa: F401 +from typing import ( + Any, + Callable, + Tuple, +) from eth_utils import ( - int_to_big_endian, big_endian_to_int, + int_to_big_endian, ) from eth_keys.constants import ( - SECPK1_N as N, + SECPK1_A as A, + SECPK1_B as B, SECPK1_G as G, + SECPK1_N as N, + SECPK1_P as P, SECPK1_Gx as Gx, SECPK1_Gy as Gy, - SECPK1_P as P, - SECPK1_A as A, - SECPK1_B as B, ) from eth_keys.exceptions import ( BadSignature, ) - -from eth_keys.utils.padding import pad32 +from eth_keys.utils.padding import ( + pad32, +) from .jacobian import ( - inv, - fast_multiply, fast_add, + fast_multiply, + from_jacobian, + inv, is_identity, jacobian_add, jacobian_multiply, - from_jacobian, ) @@ -44,10 +49,12 @@ def decode_public_key(public_key_bytes: bytes) -> Tuple[int, int]: def encode_raw_public_key(raw_public_key: Tuple[int, int]) -> bytes: left, right = raw_public_key - return b''.join(( - pad32(int_to_big_endian(left)), - pad32(int_to_big_endian(right)), - )) + return b"".join( + ( + pad32(int_to_big_endian(left)), + pad32(int_to_big_endian(right)), + ) + ) def private_key_to_public_key(private_key_bytes: bytes) -> bytes: @@ -90,15 +97,21 @@ def decompress_public_key(compressed_public_key_bytes: bytes) -> bytes: return encode_raw_public_key((x, y)) -def deterministic_generate_k(msg_hash: bytes, - private_key_bytes: bytes, - digest_fn: Callable[[], Any] = hashlib.sha256) -> int: - v_0 = b'\x01' * 32 - k_0 = b'\x00' * 32 +def deterministic_generate_k( + msg_hash: bytes, + private_key_bytes: bytes, + digest_fn: Callable[[], Any] = hashlib.sha256, +) -> int: + v_0 = b"\x01" * 32 + k_0 = b"\x00" * 32 - k_1 = hmac.new(k_0, v_0 + b'\x00' + private_key_bytes + msg_hash, digest_fn).digest() + k_1 = hmac.new( + k_0, v_0 + b"\x00" + private_key_bytes + msg_hash, digest_fn + ).digest() v_1 = hmac.new(k_1, v_0, digest_fn).digest() - k_2 = hmac.new(k_1, v_1 + b'\x01' + private_key_bytes + msg_hash, digest_fn).digest() + k_2 = hmac.new( + k_1, v_1 + b"\x01" + private_key_bytes + msg_hash, digest_fn + ).digest() v_2 = hmac.new(k_2, v_1, digest_fn).digest() kb = hmac.new(k_2, v_2, digest_fn).digest() @@ -106,8 +119,7 @@ def deterministic_generate_k(msg_hash: bytes, return k -def ecdsa_raw_sign(msg_hash: bytes, - private_key_bytes: bytes) -> Tuple[int, int, int]: +def ecdsa_raw_sign(msg_hash: bytes, private_key_bytes: bytes) -> Tuple[int, int, int]: z = big_endian_to_int(msg_hash) k = deterministic_generate_k(msg_hash, private_key_bytes) @@ -120,9 +132,9 @@ def ecdsa_raw_sign(msg_hash: bytes, return v - 27, r, s -def ecdsa_raw_verify(msg_hash: bytes, - rs: Tuple[int, int], - public_key_bytes: bytes) -> bool: +def ecdsa_raw_verify( + msg_hash: bytes, rs: Tuple[int, int], public_key_bytes: bytes +) -> bool: raw_public_key = decode_public_key(public_key_bytes) r, s = rs @@ -138,13 +150,12 @@ def ecdsa_raw_verify(msg_hash: bytes, return bool(r == x and (r % N) and (s % N)) -def ecdsa_raw_recover(msg_hash: bytes, - vrs: Tuple[int, int, int]) -> bytes: +def ecdsa_raw_recover(msg_hash: bytes, vrs: Tuple[int, int, int]) -> bytes: v, r, s = vrs v += 27 if not (27 <= v <= 34): - raise BadSignature("%d must in range 27-31" % v) + raise BadSignature(f"{v} must in range 27-31") x = r diff --git a/eth_keys/backends/native/jacobian.py b/eth_keys/backends/native/jacobian.py index 1ea09fe..383a240 100644 --- a/eth_keys/backends/native/jacobian.py +++ b/eth_keys/backends/native/jacobian.py @@ -1,10 +1,12 @@ -from typing import Tuple # noqa: F401 +from typing import ( + Tuple, +) from eth_keys.constants import ( IDENTITY_POINTS, - SECPK1_P as P, - SECPK1_N as N, SECPK1_A as A, + SECPK1_N as N, + SECPK1_P as P, ) @@ -32,13 +34,14 @@ def jacobian_double(p: Tuple[int, int, int]) -> Tuple[int, int, int]: S = (4 * p[0] * ysq) % P M = (3 * p[0] ** 2 + A * p[2] ** 4) % P nx = (M**2 - 2 * S) % P - ny = (M * (S - nx) - 8 * ysq ** 2) % P + ny = (M * (S - nx) - 8 * ysq**2) % P nz = (2 * p[1] * p[2]) % P return (nx, ny, nz) -def jacobian_add(p: Tuple[int, int, int], - q: Tuple[int, int, int]) -> Tuple[int, int, int]: +def jacobian_add( + p: Tuple[int, int, int], q: Tuple[int, int, int] +) -> Tuple[int, int, int]: if not p[1]: return q if not q[1]: @@ -56,7 +59,7 @@ def jacobian_add(p: Tuple[int, int, int], H2 = (H * H) % P H3 = (H * H2) % P U1H2 = (U1 * H2) % P - nx = (R ** 2 - H3 - 2 * U1H2) % P + nx = (R**2 - H3 - 2 * U1H2) % P ny = (R * (U1H2 - nx) - S1 * H3) % P nz = (H * p[2] * q[2]) % P return (nx, ny, nz) @@ -67,8 +70,7 @@ def from_jacobian(p: Tuple[int, int, int]) -> Tuple[int, int]: return ((p[0] * z**2) % P, (p[1] * z**3) % P) -def jacobian_multiply(a: Tuple[int, int, int], - n: int) -> Tuple[int, int, int]: +def jacobian_multiply(a: Tuple[int, int, int], n: int) -> Tuple[int, int, int]: if a[1] == 0 or n == 0: return (0, 0, 1) if n == 1: @@ -83,13 +85,11 @@ def jacobian_multiply(a: Tuple[int, int, int], raise Exception("Invariant: Unreachable code path") -def fast_multiply(a: Tuple[int, int], - n: int) -> Tuple[int, int]: +def fast_multiply(a: Tuple[int, int], n: int) -> Tuple[int, int]: return from_jacobian(jacobian_multiply(to_jacobian(a), n)) -def fast_add(a: Tuple[int, int], - b: Tuple[int, int]) -> Tuple[int, int]: +def fast_add(a: Tuple[int, int], b: Tuple[int, int]) -> Tuple[int, int]: return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b))) diff --git a/eth_keys/backends/native/main.py b/eth_keys/backends/native/main.py index ca7094f..8237ceb 100644 --- a/eth_keys/backends/native/main.py +++ b/eth_keys/backends/native/main.py @@ -1,18 +1,7 @@ -from __future__ import absolute_import - -from typing import Optional # noqa: F401 - -from .ecdsa import ( - ecdsa_raw_recover, - ecdsa_raw_sign, - ecdsa_raw_verify, - private_key_to_public_key, - compress_public_key, - decompress_public_key, +from eth_keys.backends.base import ( + BaseECCBackend, ) - -from eth_keys.backends.base import BaseECCBackend -from eth_keys.datatypes import ( # noqa: F401 +from eth_keys.datatypes import ( BaseSignature, NonRecoverableSignature, PrivateKey, @@ -20,31 +9,35 @@ Signature, ) +from .ecdsa import ( + compress_public_key, + decompress_public_key, + ecdsa_raw_recover, + ecdsa_raw_sign, + ecdsa_raw_verify, + private_key_to_public_key, +) + class NativeECCBackend(BaseECCBackend): - def ecdsa_sign(self, - msg_hash: bytes, - private_key: PrivateKey) -> Signature: + def ecdsa_sign(self, msg_hash: bytes, private_key: PrivateKey) -> Signature: signature_vrs = ecdsa_raw_sign(msg_hash, private_key.to_bytes()) signature = Signature(vrs=signature_vrs, backend=self) return signature - def ecdsa_sign_non_recoverable(self, - msg_hash: bytes, - private_key: PrivateKey) -> NonRecoverableSignature: + def ecdsa_sign_non_recoverable( + self, msg_hash: bytes, private_key: PrivateKey + ) -> NonRecoverableSignature: _, signature_r, signature_s = ecdsa_raw_sign(msg_hash, private_key.to_bytes()) signature = NonRecoverableSignature(rs=(signature_r, signature_s), backend=self) return signature - def ecdsa_verify(self, - msg_hash: bytes, - signature: BaseSignature, - public_key: PublicKey) -> bool: + def ecdsa_verify( + self, msg_hash: bytes, signature: BaseSignature, public_key: PublicKey + ) -> bool: return ecdsa_raw_verify(msg_hash, signature.rs, public_key.to_bytes()) - def ecdsa_recover(self, - msg_hash: bytes, - signature: Signature) -> PublicKey: + def ecdsa_recover(self, msg_hash: bytes, signature: Signature) -> PublicKey: public_key_bytes = ecdsa_raw_recover(msg_hash, signature.vrs) public_key = PublicKey(public_key_bytes, backend=self) return public_key @@ -54,10 +47,8 @@ def private_key_to_public_key(self, private_key: PrivateKey) -> PublicKey: public_key = PublicKey(public_key_bytes, backend=self) return public_key - def decompress_public_key_bytes(self, - compressed_public_key_bytes: bytes) -> bytes: + def decompress_public_key_bytes(self, compressed_public_key_bytes: bytes) -> bytes: return decompress_public_key(compressed_public_key_bytes) - def compress_public_key_bytes(self, - uncompressed_public_key_bytes: bytes) -> bytes: + def compress_public_key_bytes(self, uncompressed_public_key_bytes: bytes) -> bytes: return compress_public_key(uncompressed_public_key_bytes) diff --git a/eth_keys/constants.py b/eth_keys/constants.py index b7757ce..9b62f00 100644 --- a/eth_keys/constants.py +++ b/eth_keys/constants.py @@ -1,16 +1,23 @@ -from typing import Tuple # noqa: F401 - +from typing import ( + Tuple, +) # # SECPK1N # -SECPK1_P = 2**256 - 2**32 - 977 # type: int -SECPK1_N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 # type: int # noqa: E501 -SECPK1_A = 0 # type: int # noqa: E501 -SECPK1_B = 7 # type: int # noqa: E501 -SECPK1_Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 # type: int # noqa: E501 -SECPK1_Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 # type: int # noqa: E501 -SECPK1_G = (SECPK1_Gx, SECPK1_Gy) # type: Tuple[int, int] +SECPK1_P: int = 2**256 - 2**32 - 977 +SECPK1_N: int = ( + 115792089237316195423570985008687907852837564279074904382605163141518161494337 +) +SECPK1_A: int = 0 +SECPK1_B: int = 7 +SECPK1_Gx: int = ( + 55066263022277343669578718895168534326250603453777594175500187360389116729240 +) +SECPK1_Gy: int = ( + 32670510020758816978083085130507043184471273380659243275938904335757337482424 +) +SECPK1_G: Tuple[int, int] = (SECPK1_Gx, SECPK1_Gy) # diff --git a/eth_keys/datatypes.py b/eth_keys/datatypes.py index 386d173..b5d4058 100644 --- a/eth_keys/datatypes.py +++ b/eth_keys/datatypes.py @@ -1,24 +1,22 @@ -from __future__ import absolute_import - from abc import ( ABC, abstractmethod, ) import codecs import collections -import sys -from typing import ( # noqa: F401 +from typing import ( + TYPE_CHECKING, Any, Tuple, - Union, Type, - TYPE_CHECKING, + Union, ) from eth_typing import ( ChecksumAddress, ) from eth_utils import ( + ValidationError, big_endian_to_int, encode_hex, int_to_big_endian, @@ -27,10 +25,11 @@ keccak, to_checksum_address, to_normalized_address, - ValidationError, - ) +from eth_keys.exceptions import ( + BadSignature, +) from eth_keys.utils.address import ( public_key_bytes_to_address, ) @@ -40,39 +39,27 @@ from eth_keys.utils.padding import ( pad32, ) - -from eth_keys.exceptions import ( - BadSignature, -) from eth_keys.validation import ( - validate_private_key_bytes, validate_compressed_public_key_bytes, - validate_uncompressed_public_key_bytes, - validate_recoverable_signature_bytes, validate_non_recoverable_signature_bytes, - validate_signature_v, + validate_private_key_bytes, + validate_recoverable_signature_bytes, validate_signature_r_or_s, + validate_signature_v, + validate_uncompressed_public_key_bytes, ) if TYPE_CHECKING: - from eth_keys.backends.base import BaseECCBackend # noqa: F401 - - -# Must compare against version_info[0] and not version_info.major to please mypy. -if sys.version_info[0] == 2: - ByteString = type( - b'BaseString', - (collections.abc.Sequence, basestring), # noqa: F821 - {}, - ) # type: Any -else: - ByteString = collections.abc.ByteString + from eth_keys.backends.base import ( + BaseECCBackend, + ) class LazyBackend: - def __init__(self, - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None, - ) -> None: + def __init__( + self, + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: from eth_keys.backends.base import ( # noqa: F811 BaseECCBackend, ) @@ -94,30 +81,33 @@ def __init__(self, self.backend = backend - _backend = None # type: BaseECCBackend + _backend: "BaseECCBackend" = None @property - def backend(self) -> 'BaseECCBackend': + def backend(self) -> "BaseECCBackend": if self._backend is None: return self.get_backend() else: return self._backend @backend.setter - def backend(self, value: 'BaseECCBackend') -> None: + def backend(self, value: "BaseECCBackend") -> None: self._backend = value @classmethod - def get_backend(cls, *args: Any, **kwargs: Any) -> 'BaseECCBackend': - from eth_keys.backends import get_backend + def get_backend(cls, *args: Any, **kwargs: Any) -> "BaseECCBackend": + from eth_keys.backends import ( + get_backend, + ) + return get_backend(*args, **kwargs) -class BaseKey(ByteString, collections.abc.Hashable): - _raw_key = None # type: bytes +class BaseKey(collections.abc.Hashable): + _raw_key: bytes = None def to_hex(self) -> str: - return '0x' + codecs.decode(codecs.encode(self._raw_key, 'hex'), 'ascii') + return "0x" + codecs.decode(codecs.encode(self._raw_key, "hex"), "ascii") def to_bytes(self) -> bytes: return self._raw_key @@ -135,13 +125,11 @@ def __len__(self) -> int: # TODO: this seems wrong. return 64 - # Must be typed with `ignore` due to - # https://github.com/python/mypy/issues/1237 - def __getitem__(self, index: int) -> int: # type: ignore + def __getitem__(self, index: int) -> int: return self._raw_key[index] def __eq__(self, other: Any) -> bool: - if hasattr(other, 'to_bytes'): + if hasattr(other, "to_bytes"): return self.to_bytes() == other.to_bytes() elif is_bytes(other): return self.to_bytes() == other @@ -149,80 +137,86 @@ def __eq__(self, other: Any) -> bool: return False def __repr__(self) -> str: - return "'{0}'".format(self.to_hex()) + return f"'{self.to_hex()}'" def __index__(self) -> int: return self.__int__() def __hex__(self) -> str: - if sys.version_info[0] == 2: - return codecs.encode(self.to_hex(), 'ascii') - else: - return self.to_hex() + return self.to_hex() class PublicKey(BaseKey, LazyBackend): - def __init__(self, - public_key_bytes: bytes, - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None, - ) -> None: + def __init__( + self, + public_key_bytes: bytes, + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: validate_uncompressed_public_key_bytes(public_key_bytes) self._raw_key = public_key_bytes super().__init__(backend=backend) @classmethod - def from_compressed_bytes(cls, - compressed_public_key_bytes: bytes, - backend: 'BaseECCBackend' = None, - ) -> 'PublicKey': + def from_compressed_bytes( + cls, + compressed_public_key_bytes: bytes, + backend: "BaseECCBackend" = None, + ) -> "PublicKey": validate_compressed_public_key_bytes(compressed_public_key_bytes) if backend is None: backend = cls.get_backend() - uncompressed_key = backend.decompress_public_key_bytes(compressed_public_key_bytes) + uncompressed_key = backend.decompress_public_key_bytes( + compressed_public_key_bytes + ) return cls(uncompressed_key, backend) @classmethod - def from_private(cls, - private_key: 'PrivateKey', - backend: 'BaseECCBackend' = None, - ) -> 'PublicKey': + def from_private( + cls, + private_key: "PrivateKey", + backend: "BaseECCBackend" = None, + ) -> "PublicKey": if backend is None: backend = cls.get_backend() return backend.private_key_to_public_key(private_key) @classmethod - def recover_from_msg(cls, - message: bytes, - signature: 'Signature', - backend: 'BaseECCBackend' = None, - ) -> 'PublicKey': + def recover_from_msg( + cls, + message: bytes, + signature: "Signature", + backend: "BaseECCBackend" = None, + ) -> "PublicKey": message_hash = keccak(message) return cls.recover_from_msg_hash(message_hash, signature, backend) @classmethod - def recover_from_msg_hash(cls, - message_hash: bytes, - signature: 'Signature', - backend: 'BaseECCBackend' = None, - ) -> 'PublicKey': + def recover_from_msg_hash( + cls, + message_hash: bytes, + signature: "Signature", + backend: "BaseECCBackend" = None, + ) -> "PublicKey": if backend is None: backend = cls.get_backend() return backend.ecdsa_recover(message_hash, signature) - def verify_msg(self, - message: bytes, - signature: 'Signature', - ) -> bool: + def verify_msg( + self, + message: bytes, + signature: "Signature", + ) -> bool: message_hash = keccak(message) return self.verify_msg_hash(message_hash, signature) - def verify_msg_hash(self, - message_hash: bytes, - signature: 'Signature', - ) -> bool: + def verify_msg_hash( + self, + message_hash: bytes, + signature: "Signature", + ) -> bool: return self.backend.ecdsa_verify(message_hash, signature, self) def to_compressed_bytes(self) -> bytes: @@ -242,12 +236,13 @@ def to_canonical_address(self) -> bytes: class PrivateKey(BaseKey, LazyBackend): - public_key = None # type: PublicKey + public_key: PublicKey = None - def __init__(self, - private_key_bytes: bytes, - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None, - ) -> None: + def __init__( + self, + private_key_bytes: bytes, + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: validate_private_key_bytes(private_key_bytes) self._raw_key = private_key_bytes @@ -255,29 +250,32 @@ def __init__(self, self.public_key = self.backend.private_key_to_public_key(self) super().__init__(backend=backend) - def sign_msg(self, message: bytes) -> 'Signature': + def sign_msg(self, message: bytes) -> "Signature": message_hash = keccak(message) return self.sign_msg_hash(message_hash) - def sign_msg_hash(self, message_hash: bytes) -> 'Signature': + def sign_msg_hash(self, message_hash: bytes) -> "Signature": return self.backend.ecdsa_sign(message_hash, self) - def sign_msg_non_recoverable(self, message: bytes) -> 'NonRecoverableSignature': + def sign_msg_non_recoverable(self, message: bytes) -> "NonRecoverableSignature": message_hash = keccak(message) return self.sign_msg_hash_non_recoverable(message_hash) - def sign_msg_hash_non_recoverable(self, message_hash: bytes) -> 'NonRecoverableSignature': + def sign_msg_hash_non_recoverable( + self, message_hash: bytes + ) -> "NonRecoverableSignature": return self.backend.ecdsa_sign_non_recoverable(message_hash, self) -class BaseSignature(ByteString, LazyBackend, ABC): - _r = None # type: int - _s = None # type: int +class BaseSignature(LazyBackend, ABC): + _r: int = None + _s: int = None - def __init__(self, - rs: Tuple[int, int], - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None - ) -> None: + def __init__( + self, + rs: Tuple[int, int], + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: for value in rs: try: validate_signature_r_or_s(value) @@ -319,20 +317,18 @@ def __len__(self) -> int: return len(bytes(self)) def __eq__(self, other: Any) -> bool: - if hasattr(other, 'to_bytes'): + if hasattr(other, "to_bytes"): return self.to_bytes() == other.to_bytes() elif is_bytes(other): return self.to_bytes() == other else: return False - # Must be typed with `ignore` due to - # https://github.com/python/mypy/issues/1237 - def __getitem__(self, index: int) -> int: # type: ignore + def __getitem__(self, index: int) -> int: return self.to_bytes()[index] def __repr__(self) -> str: - return "'{0}'".format(self.to_hex()) + return f"'{self.to_hex()}'" def __index__(self) -> int: return self.__int__() @@ -343,26 +339,23 @@ def __hex__(self) -> str: def __int__(self) -> int: return big_endian_to_int(self.to_bytes()) - def verify_msg(self, - message: bytes, - public_key: PublicKey) -> bool: + def verify_msg(self, message: bytes, public_key: PublicKey) -> bool: message_hash = keccak(message) return self.verify_msg_hash(message_hash, public_key) - def verify_msg_hash(self, - message_hash: bytes, - public_key: PublicKey) -> bool: + def verify_msg_hash(self, message_hash: bytes, public_key: PublicKey) -> bool: return self.backend.ecdsa_verify(message_hash, self, public_key) class Signature(BaseSignature): - _v = None # type: int - - def __init__(self, - signature_bytes: bytes = None, - vrs: Tuple[int, int, int] = None, - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None, - ) -> None: + _v: int = None + + def __init__( + self, + signature_bytes: bytes = None, + vrs: Tuple[int, int, int] = None, + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: if bool(signature_bytes) is bool(vrs): raise TypeError("You must provide one of `signature_bytes` or `vrs`") elif signature_bytes: @@ -371,7 +364,11 @@ def __init__(self, s = big_endian_to_int(signature_bytes[32:64]) v = ord(signature_bytes[64:65]) elif vrs: - v, r, s, = vrs + ( + v, + r, + s, + ) = vrs else: raise TypeError("Invariant: unreachable code path") @@ -411,7 +408,7 @@ def to_bytes(self) -> bytes: vb = int_to_byte(self.v) rb = pad32(int_to_big_endian(self.r)) sb = pad32(int_to_big_endian(self.s)) - return b''.join((rb, sb, vb)) + return b"".join((rb, sb, vb)) def recover_public_key_from_msg(self, message: bytes) -> PublicKey: message_hash = keccak(message) @@ -420,17 +417,17 @@ def recover_public_key_from_msg(self, message: bytes) -> PublicKey: def recover_public_key_from_msg_hash(self, message_hash: bytes) -> PublicKey: return self.backend.ecdsa_recover(message_hash, self) - def to_non_recoverable_signature(self) -> 'NonRecoverableSignature': + def to_non_recoverable_signature(self) -> "NonRecoverableSignature": return NonRecoverableSignature(rs=self.rs) class NonRecoverableSignature(BaseSignature): - - def __init__(self, - signature_bytes: bytes = None, - rs: Tuple[int, int] = None, - backend: 'Union[BaseECCBackend, Type[BaseECCBackend], str, None]' = None, - ) -> None: + def __init__( + self, + signature_bytes: bytes = None, + rs: Tuple[int, int] = None, + backend: "Union[BaseECCBackend, Type[BaseECCBackend], str, None]" = None, + ) -> None: if signature_bytes is None and rs is None: raise TypeError("You must provide one of `signature_bytes` or `vr`") elif signature_bytes: @@ -445,7 +442,4 @@ def __init__(self, super().__init__(rs=(r, s), backend=backend) def to_bytes(self) -> bytes: - return b''.join( - pad32(int_to_big_endian(value)) - for value in self.rs - ) + return b"".join(pad32(int_to_big_endian(value)) for value in self.rs) diff --git a/eth_keys/main.py b/eth_keys/main.py index 678a7b8..f6a87e1 100644 --- a/eth_keys/main.py +++ b/eth_keys/main.py @@ -1,5 +1,3 @@ -from typing import (Any, Union, Type) # noqa: F401 - from eth_utils import ( ValidationError, ) @@ -8,15 +6,14 @@ BaseSignature, LazyBackend, NonRecoverableSignature, - PublicKey, PrivateKey, + PublicKey, Signature, ) from eth_keys.validation import ( validate_message_hash, ) - # These must be aliased due to a scoping issue in mypy # https://github.com/python/mypy/issues/1775 _PublicKey = PublicKey @@ -26,24 +23,20 @@ class KeyAPI(LazyBackend): - # - # datatype shortcuts - # - PublicKey = PublicKey # type: Type[_PublicKey] - PrivateKey = PrivateKey # type: Type[_PrivateKey] - Signature = Signature # type: Type[_Signature] - NonRecoverableSignature = NonRecoverableSignature # type: Type[_NonRecoverableSignature] + PublicKey = PublicKey + PrivateKey = PrivateKey + Signature = Signature + NonRecoverableSignature = NonRecoverableSignature # # Proxy method calls to the backends # - def ecdsa_sign(self, - message_hash: bytes, - private_key: _PrivateKey) -> _Signature: + def ecdsa_sign(self, message_hash: bytes, private_key: _PrivateKey) -> _Signature: validate_message_hash(message_hash) if not isinstance(private_key, PrivateKey): raise ValidationError( - "The `private_key` must be an instance of `eth_keys.datatypes.PrivateKey`" + "The `private_key` must be an instance of " + "`eth_keys.datatypes.PrivateKey`" ) signature = self.backend.ecdsa_sign(message_hash, private_key) if not isinstance(signature, Signature): @@ -53,13 +46,14 @@ def ecdsa_sign(self, ) return signature - def ecdsa_sign_non_recoverable(self, - message_hash: bytes, - private_key: _PrivateKey) -> _NonRecoverableSignature: + def ecdsa_sign_non_recoverable( + self, message_hash: bytes, private_key: _PrivateKey + ) -> _NonRecoverableSignature: validate_message_hash(message_hash) if not isinstance(private_key, PrivateKey): raise ValidationError( - "The `private_key` must be an instance of `eth_keys.datatypes.PrivateKey`" + "The `private_key` must be an instance of " + "`eth_keys.datatypes.PrivateKey`" ) signature = self.backend.ecdsa_sign_non_recoverable(message_hash, private_key) if not isinstance(signature, NonRecoverableSignature): @@ -69,10 +63,9 @@ def ecdsa_sign_non_recoverable(self, ) return signature - def ecdsa_verify(self, - message_hash: bytes, - signature: BaseSignature, - public_key: _PublicKey) -> bool: + def ecdsa_verify( + self, message_hash: bytes, signature: BaseSignature, public_key: _PublicKey + ) -> bool: validate_message_hash(message_hash) if not isinstance(public_key, PublicKey): raise ValidationError( @@ -80,13 +73,12 @@ def ecdsa_verify(self, ) if not isinstance(signature, BaseSignature): raise ValidationError( - "The `signature` must be an instance of `eth_keys.datatypes.BaseSignature`" + "The `signature` must be an instance of " + "`eth_keys.datatypes.BaseSignature`" ) return self.backend.ecdsa_verify(message_hash, signature, public_key) - def ecdsa_recover(self, - message_hash: bytes, - signature: _Signature) -> _PublicKey: + def ecdsa_recover(self, message_hash: bytes, signature: _Signature) -> _PublicKey: validate_message_hash(message_hash) if not isinstance(signature, Signature): raise ValidationError( @@ -103,7 +95,8 @@ def ecdsa_recover(self, def private_key_to_public_key(self, private_key: _PrivateKey) -> _PublicKey: if not isinstance(private_key, PrivateKey): raise ValidationError( - "The `private_key` must be an instance of `eth_keys.datatypes.PrivateKey`" + "The `private_key` must be an instance of " + "`eth_keys.datatypes.PrivateKey`" ) public_key = self.backend.private_key_to_public_key(private_key) if not isinstance(public_key, PublicKey): diff --git a/eth_keys/tools/factories.py b/eth_keys/tools/factories.py index ffc01bb..c4a3ebb 100644 --- a/eth_keys/tools/factories.py +++ b/eth_keys/tools/factories.py @@ -6,7 +6,9 @@ "which does not appear to be installed." ) from err -from eth_keys import keys +from eth_keys import ( + keys, +) def _mk_random_bytes(num_bytes: int) -> bytes: @@ -14,6 +16,7 @@ def _mk_random_bytes(num_bytes: int) -> bytes: import secrets except ImportError: import os + return os.urandom(num_bytes) else: return secrets.token_bytes(num_bytes) @@ -30,4 +33,6 @@ class PublicKeyFactory(factory.Factory): # type: ignore class Meta: model = keys.PublicKey - public_key_bytes = factory.LazyFunction(lambda: PrivateKeyFactory().public_key.to_bytes()) + public_key_bytes = factory.LazyFunction( + lambda: PrivateKeyFactory().public_key.to_bytes() + ) diff --git a/eth_keys/utils/der.py b/eth_keys/utils/der.py index ddeee83..c06e591 100644 --- a/eth_keys/utils/der.py +++ b/eth_keys/utils/der.py @@ -1,8 +1,9 @@ # Non-recoverable signatures are encoded using a DER sequence of two integers # We locally implement serialization and deserialization for this specific spec # with constrained inputs. -# This is done locally to avoid importing a 3rd-party library, in this very sensitive project. -# asn1tools and pyasn1 were used as reference APIs, see how in tests/core/test_utils_asn1.py +# This is done locally to avoid importing a 3rd-party library, in this very sensitive +# project. asn1tools and pyasn1 were used as reference APIs, see how in +# tests/core/test_utils_asn1.py # # See more about DER encodings, and ASN.1 in general, here: # http://luca.ntop.org/Teaching/Appunti/asn1.html @@ -73,14 +74,19 @@ def two_int_sequence_decoder(encoded: bytes) -> Tuple[int, int]: See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-sequence """ if encoded[0] != 0x30: - raise ValueError("Encoded sequence must start with 0x30 byte, but got %s" % encoded[0]) + raise ValueError( + f"Encoded sequence must start with 0x30 byte, but got {encoded[0]}" + ) # skip sequence length int1, rest = _decode_int(encoded[2:]) int2, empty = _decode_int(rest) if len(empty) != 0: - raise ValueError("Encoded sequence must not contain any trailing data, but had %r" % empty) + raise ValueError( + "Encoded sequence must not contain any trailing data, but had " + f"{repr(empty)}" + ) return int1, int2 @@ -94,7 +100,8 @@ def _encode_int(primitive: int) -> Iterator[int]: encoded = int_to_big_endian(primitive) if encoded[0] >= 128: - # Indicate that integer is positive (it always is, but doesn't always need the flag) + # Indicate that integer is positive + # (it always is, but doesn't always need the flag) yield len(encoded) + 1 yield 0x00 else: @@ -108,11 +115,12 @@ def _decode_int(encoded: bytes) -> Tuple[int, bytes]: if encoded[0] != 0x02: raise ValueError( - "Encoded value must be an integer, starting with on 0x02 byte, but got %s" % encoded[0] + "Encoded value must be an integer, starting with on 0x02 byte, but got " + f"{encoded[0]}" ) length = encoded[1] # to_int can handle leading zeros - decoded_int = big_endian_to_int(encoded[2:2 + length]) + decoded_int = big_endian_to_int(encoded[2 : 2 + length]) - return decoded_int, encoded[2 + length:] + return decoded_int, encoded[2 + length :] diff --git a/eth_keys/utils/module_loading.py b/eth_keys/utils/module_loading.py index 03b4a78..308acea 100644 --- a/eth_keys/utils/module_loading.py +++ b/eth_keys/utils/module_loading.py @@ -1,6 +1,11 @@ +from importlib import ( + import_module, +) import operator -from typing import (Any, Tuple) -from importlib import import_module +from typing import ( + Any, + Tuple, +) def import_string(dotted_path: str) -> Any: @@ -10,9 +15,9 @@ def import_string(dotted_path: str) -> Any: last name in the path. Raise ImportError if the import failed. """ try: - module_path, class_name = dotted_path.rsplit('.', 1) + module_path, class_name = dotted_path.rsplit(".", 1) except ValueError: - msg = "%s doesn't look like a module path" % dotted_path + msg = f"{dotted_path} doesn't look like a module path" raise ImportError(msg) module = import_module(module_path) @@ -20,18 +25,17 @@ def import_string(dotted_path: str) -> Any: try: return getattr(module, class_name) except AttributeError: - msg = 'Module "%s" does not define a "%s" attribute/class' % ( - module_path, class_name) + msg = f'Module "{module_path}" does not define a "{class_name}" attribute/class' raise ImportError(msg) def split_at_longest_importable_path(dotted_path: str) -> Tuple[str, str]: - num_path_parts = len(dotted_path.split('.')) + num_path_parts = len(dotted_path.split(".")) for i in range(1, num_path_parts): - path_parts = dotted_path.rsplit('.', i) + path_parts = dotted_path.rsplit(".", i) import_part = path_parts[0] - remainder = '.'.join(path_parts[1:]) + remainder = ".".join(path_parts[1:]) try: module = import_module(import_part) @@ -42,11 +46,9 @@ def split_at_longest_importable_path(dotted_path: str) -> Tuple[str, str]: operator.attrgetter(remainder)(module) except AttributeError: raise ImportError( - "Unable to derive appropriate import path for {0}".format( - dotted_path, - ) + f"Unable to derive appropriate import path for {dotted_path}" ) else: return import_part, remainder else: - return '', dotted_path + return "", dotted_path diff --git a/eth_keys/utils/numeric.py b/eth_keys/utils/numeric.py index 9041e0a..42a86a1 100644 --- a/eth_keys/utils/numeric.py +++ b/eth_keys/utils/numeric.py @@ -8,7 +8,8 @@ def int_to_byte(value: int) -> bytes: def coerce_low_s(value: int) -> int: - """Coerce the s component of an ECDSA signature into its low-s form. + """ + Coerce the s component of an ECDSA signature into its low-s form. See https://bitcoin.stackexchange.com/questions/83408/in-ecdsa-why-is-r-%E2%88%92s-mod-n-complementary-to-r-s # noqa: E501 or https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md. diff --git a/eth_keys/utils/padding.py b/eth_keys/utils/padding.py index 58665bf..d03fcbd 100644 --- a/eth_keys/utils/padding.py +++ b/eth_keys/utils/padding.py @@ -1,2 +1,2 @@ def pad32(value: bytes) -> bytes: - return value.rjust(32, b'\x00') + return value.rjust(32, b"\x00") diff --git a/eth_keys/validation.py b/eth_keys/validation.py index 96ae759..878adda 100644 --- a/eth_keys/validation.py +++ b/eth_keys/validation.py @@ -1,12 +1,16 @@ -from typing import Any +from typing import ( + Any, +) from eth_utils import ( + ValidationError, encode_hex, is_bytes, is_integer, - ValidationError, ) -from eth_utils.toolz import curry +from eth_utils.toolz import ( + curry, +) from eth_keys.constants import ( SECPK1_N, @@ -15,12 +19,12 @@ def validate_integer(value: Any) -> None: if not is_integer(value) or isinstance(value, bool): - raise ValidationError("Value must be a an integer. Got: {0}".format(type(value))) + raise ValidationError(f"Value must be a an integer. Got: {type(value)}") def validate_bytes(value: Any) -> None: if not is_bytes(value): - raise ValidationError("Value must be a byte string. Got: {0}".format(type(value))) + raise ValidationError(f"Value must be a byte string. Got: {type(value)}") @curry @@ -28,9 +32,7 @@ def validate_gte(value: Any, minimum: int) -> None: validate_integer(value) if value < minimum: raise ValidationError( - "Value {0} is not greater than or equal to {1}".format( - value, minimum, - ) + f"Value {value} is not greater than or equal to {minimum}" ) @@ -38,11 +40,7 @@ def validate_gte(value: Any, minimum: int) -> None: def validate_lte(value: Any, maximum: int) -> None: validate_integer(value) if value > maximum: - raise ValidationError( - "Value {0} is not less than or equal to {1}".format( - value, maximum, - ) - ) + raise ValidationError(f"Value {value} is not less than or equal to {maximum}") validate_lt_secpk1n = validate_lte(maximum=SECPK1_N - 1) @@ -52,12 +50,8 @@ def validate_bytes_length(value: bytes, expected_length: int, name: str) -> None actual_length = len(value) if actual_length != expected_length: raise ValidationError( - "Unexpected {name} length: Expected {expected_length}, but got {actual_length} " - "bytes".format( - name=name, - expected_length=expected_length, - actual_length=actual_length, - ) + f"Unexpected {name} length: Expected {expected_length}, but got " + f"{actual_length} bytes" ) @@ -77,10 +71,8 @@ def validate_compressed_public_key_bytes(value: Any) -> None: first_byte = value[0:1] if first_byte not in (b"\x02", b"\x03"): raise ValidationError( - "Unexpected compressed public key format: Must start with 0x02 or 0x03, but starts " - "with {first_byte}".format( - first_byte=encode_hex(first_byte), - ) + "Unexpected compressed public key format: Must start with 0x02 or 0x03, " + f"but starts with {encode_hex(first_byte)}" ) diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 457f52c..0000000 --- a/mypy.ini +++ /dev/null @@ -1,15 +0,0 @@ -[mypy] - -check_untyped_defs = True -disallow_incomplete_defs = True -disallow_untyped_defs = True -disallow_any_generics = True -disallow_untyped_calls = True -disallow_subclassing_any = True -ignore_missing_imports = True -strict_optional = False -strict_equality = True -warn_redundant_casts = True -warn_unused_configs = True -warn_unused_ignores = True - diff --git a/pyproject.toml b/pyproject.toml index 023f5d2..4497cd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,32 +1,32 @@ [tool.autoflake] -remove_all_unused_imports = "True" +remove_all_unused_imports = true exclude = "__init__.py" [tool.isort] -combine_as_imports = "True" +combine_as_imports = true extra_standard_library = "pytest" force_grid_wrap = 1 -force_sort_within_sections = "True" +force_sort_within_sections = true known_third_party = "hypothesis,pytest" known_first_party = "eth_keys" multi_line_output = 3 profile = "black" [tool.mypy] -check_untyped_defs = "True" -disallow_incomplete_defs = "True" -disallow_untyped_defs = "True" -disallow_any_generics = "True" -disallow_untyped_calls = "True" -disallow_untyped_decorators = "True" -disallow_subclassing_any = "True" -ignore_missing_imports = "True" -strict_optional = "True" -strict_equality = "True" -warn_redundant_casts = "True" -warn_return_any = "True" -warn_unused_configs = "True" -warn_unused_ignores = "True" +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_untyped_decorators = false +disallow_subclassing_any = true +ignore_missing_imports = true +strict_optional = false +strict_equality = true +warn_redundant_casts = true +warn_return_any = false +warn_unused_configs = true +warn_unused_ignores = true [tool.pydocstyle] @@ -63,7 +63,7 @@ add-ignore = "D200,D203,D204,D205,D212,D302,D400,D401,D412,D415" [tool.pytest.ini_options] addopts = "-v --showlocals --durations 10" -xfail_strict = "True" +xfail_strict = true log_format = "%(levelname)8s %(asctime)s %(filename)20s %(message)s" log_date_format = "%m-%d %H:%M:%S" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index beaabb6..0000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts= -v --showlocals diff --git a/setup.py b/setup.py index 421b671..879a294 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- from setuptools import ( find_packages, setup, @@ -7,7 +6,7 @@ extras_require = { "coincurve": [ - "coincurve>=7.0.0,<16.0.0", + "coincurve>=12.0.0", ], "dev": [ "build>=0.9.0", @@ -23,18 +22,19 @@ ], "test": [ "pytest>=7.0.0", - "pytest-xdist>=2.4.0", - "asn1tools>=0.146.2,<0.147", - "factory-boy>=3.0.1,<3.1", - "pyasn1>=0.4.5,<0.5", - "hypothesis>=5.10.3, <6.0.0", - "eth-hash[pysha3];implementation_name=='cpython'", - "eth-hash[pycryptodome];implementation_name=='pypy'", + "asn1tools>=0.146.2", + "factory-boy>=3.0.1", + "pyasn1>=0.4.5", + "hypothesis>=5.10.3,<6", + "eth-hash[pysha3]", ], } extras_require["dev"] = ( - extras_require["coincurve"] + extras_require["dev"] + extras_require["docs"] + extras_require["test"] + extras_require["coincurve"] + + extras_require["dev"] + + extras_require["docs"] + + extras_require["test"] ) @@ -45,7 +45,7 @@ setup( name="eth-keys", # *IMPORTANT*: Don't manually change the version here. Use `make bump`, as described in readme - version="0.1.0-alpha.0", + version="0.4.0", description="""eth-keys: Common API for Ethereum key operations""", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/backends/conftest.py b/tests/backends/conftest.py index 613f7b8..8aed26e 100644 --- a/tests/backends/conftest.py +++ b/tests/backends/conftest.py @@ -1,18 +1,16 @@ -import pytest - from eth_utils import ( decode_hex, keccak, ) +import pytest - -MSG = b'message' +MSG = b"message" MSGHASH = keccak(MSG) # This is a sample of signatures generated with a known-good implementation of the ECDSA -# algorithm, which we use to test our ECC backends. If necessary, it can be generated from scratch -# with the following code: +# algorithm, which we use to test our ECC backends. If necessary, it can be generated +# from scratch with the following code: """ from devp2p import crypto from eth_utils import encode_hex @@ -35,48 +33,72 @@ SECRETS = { "alice": dict( - privkey=decode_hex('0x9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501'), - pubkey=decode_hex('0x5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca'), # noqa: E501 - compressed_pubkey=decode_hex('0x025eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b42'), # noqa: E501 - sig=decode_hex('0xb20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01'), # noqa: E501 + privkey=decode_hex( + "0x9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501" + ), + pubkey=decode_hex( + "0x5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca" # noqa: E501 + ), + compressed_pubkey=decode_hex( + "0x025eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b42" + ), + sig=decode_hex( + "0xb20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01" # noqa: E501 + ), raw_sig=( 1, - 80536744857756143861726945576089915884233437828013729338039544043241440681784, - 1902566422691403459035240420865094128779958320521066670269403689808757640701, - ) + 80536744857756143861726945576089915884233437828013729338039544043241440681784, # noqa: E501 + 1902566422691403459035240420865094128779958320521066670269403689808757640701, # noqa: E501 + ), ), "bob": dict( - privkey=decode_hex('0x38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2'), - pubkey=decode_hex('0x347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570'), # noqa: E501 - compressed_pubkey=decode_hex('0x02347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937'), # noqa: E501 - sig=decode_hex('0x5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601'), # noqa: E501 + privkey=decode_hex( + "0x38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2" + ), + pubkey=decode_hex( + "0x347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570" # noqa: E501 + ), + compressed_pubkey=decode_hex( + "0x02347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937" + ), + sig=decode_hex( + "0x5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601" # noqa: E501 + ), raw_sig=( 1, - 41741612198399299636429810387160790514780876799439767175315078161978521003886, - 47545396818609319588074484786899049290652725314938191835667190243225814114102, + 41741612198399299636429810387160790514780876799439767175315078161978521003886, # noqa: E501 + 47545396818609319588074484786899049290652725314938191835667190243225814114102, # noqa: E501 ), ), "eve": dict( - privkey=decode_hex('0x876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c'), - pubkey=decode_hex('0xc06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0'), # noqa: E501 - compressed_pubkey=decode_hex('0x02c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91'), # noqa: E501 - sig=decode_hex('0xbabeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00'), # noqa: E501 + privkey=decode_hex( + "0x876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c" + ), + pubkey=decode_hex( + "0xc06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0" # noqa: E501 + ), + compressed_pubkey=decode_hex( + "0x02c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91" + ), + sig=decode_hex( + "0xbabeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00" # noqa: E501 + ), raw_sig=( 0, - 84467545608142925331782333363288012579669270632210954476013542647119929595395, - 43529886636775750164425297556346136250671451061152161143648812009114516499167, + 84467545608142925331782333363288012579669270632210954476013542647119929595395, # noqa: E501 + 43529886636775750164425297556346136250671451061152161143648812009114516499167, # noqa: E501 ), ), } -@pytest.fixture(params=['alice', 'bob', 'eve']) +@pytest.fixture(params=["alice", "bob", "eve"]) def key_fixture(request): - if request.param == 'alice': - return SECRETS['alice'] - elif request.param == 'bob': - return SECRETS['bob'] - elif request.param == 'eve': - return SECRETS['eve'] + if request.param == "alice": + return SECRETS["alice"] + elif request.param == "bob": + return SECRETS["bob"] + elif request.param == "eve": + return SECRETS["eve"] else: - assert False, "Should be unreachable" + raise AssertionError("Should be unreachable") diff --git a/tests/backends/strategies.py b/tests/backends/strategies.py index 6e896b7..f7b32cb 100644 --- a/tests/backends/strategies.py +++ b/tests/backends/strategies.py @@ -1,10 +1,9 @@ -from hypothesis import ( - strategies as st, -) - from eth_utils import ( int_to_big_endian, ) +from hypothesis import ( + strategies as st, +) from eth_keys.constants import ( SECPK1_N, @@ -13,10 +12,13 @@ pad32, ) - -private_key_st = st.integers(min_value=1, max_value=SECPK1_N).map( - int_to_big_endian, -).map(pad32) +private_key_st = ( + st.integers(min_value=1, max_value=SECPK1_N) + .map( + int_to_big_endian, + ) + .map(pad32) +) message_hash_st = st.binary(min_size=32, max_size=32) diff --git a/tests/backends/test_backends.py b/tests/backends/test_backends.py index 2ba8c19..b2e1e3e 100644 --- a/tests/backends/test_backends.py +++ b/tests/backends/test_backends.py @@ -1,33 +1,35 @@ import os -import pytest - +from eth_utils import ( + keccak, +) from hypothesis import ( given, ) +import pytest +from strategies import ( + message_hash_st, + private_key_st, +) -from eth_keys import KeyAPI -from eth_keys.backends import CoinCurveECCBackend -from eth_keys.backends import NativeECCBackend +from eth_keys import ( + KeyAPI, +) +from eth_keys.backends import ( + CoinCurveECCBackend, + NativeECCBackend, +) from eth_keys.constants import ( SECPK1_N, ) -from eth_keys.exceptions import BadSignature +from eth_keys.exceptions import ( + BadSignature, +) from eth_keys.utils.numeric import ( coerce_low_s, ) -from eth_utils import ( - keccak, -) - -from strategies import ( - private_key_st, - message_hash_st, -) - - -MSG = b'message' +MSG = b"message" MSGHASH = keccak(MSG) @@ -36,10 +38,11 @@ ] try: - import coincurve + pass + backends.append(CoinCurveECCBackend()) except ImportError: - if 'REQUIRE_COINCURVE' in os.environ: + if "REQUIRE_COINCURVE" in os.environ: raise @@ -47,20 +50,20 @@ def backend_id_fn(backend): return type(backend).__name__ -@pytest.fixture(params=backends, ids=backend_id_fn, scope='module') +@pytest.fixture(params=backends, ids=backend_id_fn, scope="module") def key_api(request): return KeyAPI(backend=request.param) def test_ecdsa_sign(key_api, key_fixture): - private_key = key_api.PrivateKey(key_fixture['privkey']) + private_key = key_api.PrivateKey(key_fixture["privkey"]) signature = key_api.ecdsa_sign(MSGHASH, private_key) assert key_api.ecdsa_verify(MSGHASH, signature, private_key.public_key) def test_ecdsa_sign_non_recoverable(key_api, key_fixture): - private_key = key_api.PrivateKey(key_fixture['privkey']) + private_key = key_api.PrivateKey(key_fixture["privkey"]) signature = key_api.ecdsa_sign_non_recoverable(MSGHASH, private_key) non_recoverable_signature = key_api.ecdsa_sign_non_recoverable(MSGHASH, private_key) assert non_recoverable_signature.r == signature.r @@ -70,35 +73,43 @@ def test_ecdsa_sign_non_recoverable(key_api, key_fixture): def test_ecdsa_verify(key_api, key_fixture): - signature = key_api.Signature(vrs=key_fixture['raw_sig']) - public_key = key_api.PublicKey(key_fixture['pubkey']) + signature = key_api.Signature(vrs=key_fixture["raw_sig"]) + public_key = key_api.PublicKey(key_fixture["pubkey"]) assert key_api.ecdsa_verify(MSGHASH, signature, public_key) def test_ecdsa_recover(key_api, key_fixture): - signature = key_api.Signature(vrs=key_fixture['raw_sig']) - public_key = key_api.PublicKey(key_fixture['pubkey']) + signature = key_api.Signature(vrs=key_fixture["raw_sig"]) + public_key = key_api.PublicKey(key_fixture["pubkey"]) assert key_api.ecdsa_recover(MSGHASH, signature) == public_key @pytest.mark.parametrize( - 'v,r,s,msghash', + "v,r,s,msghash", ( ( # Test params from the ethereum/tests repo 0, - int("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16), - int("0x6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9", 16), - bytes.fromhex("6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9") + int( + "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16 + ), + int( + "0x6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9", 16 + ), + bytes.fromhex( + "6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9" + ), ), ( # Test params from signing a message using the private key: '0x' + '00' * 32 1, - 29836180350949573232951565573845061551093497675544587026406064656720118638890, - 36811185137926304485684021052401800813557229270017380070056792973957337676018, - bytes.fromhex('1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'), + 29836180350949573232951565573845061551093497675544587026406064656720118638890, # noqa: E501 + 36811185137926304485684021052401800813557229270017380070056792973957337676018, # noqa: E501 + bytes.fromhex( + "1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750" + ), ), ), ) @@ -106,20 +117,20 @@ def test_ecdsa_recover_identity_point(key_api, key_fixture, v, r, s, msghash): signature = key_api.Signature(vrs=(v, r, s)) with pytest.raises(BadSignature): - key_api.ecdsa_recover(msghash, signature) + key_api.ecdsa_recover(msghash, signature) def test_decompress_public_key_bytes(key_api, key_fixture): - compressed = key_fixture['compressed_pubkey'] - uncompressed = key_fixture['pubkey'] + compressed = key_fixture["compressed_pubkey"] + uncompressed = key_fixture["pubkey"] key_from_compressed = key_api.PublicKey.from_compressed_bytes(compressed) assert key_from_compressed.to_bytes() == uncompressed def test_compress_public_key_bytes(key_api, key_fixture): - uncompressed = key_fixture['pubkey'] - compressed = key_fixture['compressed_pubkey'] + uncompressed = key_fixture["pubkey"] + compressed = key_fixture["compressed_pubkey"] key_from_uncompressed = key_api.PublicKey(uncompressed) assert key_from_uncompressed.to_compressed_bytes() == compressed @@ -147,5 +158,7 @@ def test_signatures_with_high_s(key_api, private_key_bytes, message_hash): assert coerce_low_s(low_s_signature.s) == low_s_signature.s high_s = -low_s_signature.s % SECPK1_N assert coerce_low_s(high_s) == low_s_signature.s - high_s_signature = key_api.Signature(vrs=(low_s_signature.v, low_s_signature.r, high_s)) + high_s_signature = key_api.Signature( + vrs=(low_s_signature.v, low_s_signature.r, high_s) + ) assert key_api.ecdsa_verify(message_hash, high_s_signature, private_key.public_key) diff --git a/tests/backends/test_native_backend_against_coincurve.py b/tests/backends/test_native_backend_against_coincurve.py index 2980386..e89ead0 100644 --- a/tests/backends/test_native_backend_against_coincurve.py +++ b/tests/backends/test_native_backend_against_coincurve.py @@ -1,42 +1,41 @@ -import pytest - +from eth_utils import ( + keccak, +) from hypothesis import ( given, settings, strategies as st, ) - -from eth_utils import ( - keccak, -) - -from eth_keys.exceptions import ( - BadSignature, -) - -from eth_keys import KeyAPI -from eth_keys.backends import CoinCurveECCBackend -from eth_keys.backends import NativeECCBackend - +import pytest from strategies import ( - private_key_st, message_hash_st, + private_key_st, signature_st, ) +from eth_keys import ( + KeyAPI, +) +from eth_keys.backends import ( + CoinCurveECCBackend, + NativeECCBackend, +) +from eth_keys.exceptions import ( + BadSignature, +) -MSG = b'message' +MSG = b"message" MSGHASH = keccak(MSG) MAX_EXAMPLES = 200 -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def native_key_api(): return KeyAPI(backend=NativeECCBackend()) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def coincurve_key_api(): return KeyAPI(backend=CoinCurveECCBackend()) @@ -45,9 +44,9 @@ def coincurve_key_api(): private_key_bytes=private_key_st, ) @settings(max_examples=MAX_EXAMPLES) -def test_public_key_generation_is_equal(private_key_bytes, - native_key_api, - coincurve_key_api): +def test_public_key_generation_is_equal( + private_key_bytes, native_key_api, coincurve_key_api +): native_public_key = native_key_api.PrivateKey(private_key_bytes).public_key coincurve_public_key = coincurve_key_api.PrivateKey(private_key_bytes).public_key @@ -59,10 +58,9 @@ def test_public_key_generation_is_equal(private_key_bytes, message_hash=message_hash_st, ) @settings(max_examples=MAX_EXAMPLES) -def test_signing_is_equal(private_key_bytes, - message_hash, - native_key_api, - coincurve_key_api): +def test_signing_is_equal( + private_key_bytes, message_hash, native_key_api, coincurve_key_api +): native_private_key = native_key_api.PrivateKey(private_key_bytes) native_signature = native_key_api.ecdsa_sign(message_hash, native_private_key) native_non_recoverable_signature = native_key_api.ecdsa_sign_non_recoverable( @@ -71,7 +69,9 @@ def test_signing_is_equal(private_key_bytes, ) coincurve_private_key = coincurve_key_api.PrivateKey(private_key_bytes) - coincurve_signature = coincurve_key_api.ecdsa_sign(message_hash, coincurve_private_key) + coincurve_signature = coincurve_key_api.ecdsa_sign( + message_hash, coincurve_private_key + ) coincurve_non_recoverable_signature = coincurve_key_api.ecdsa_sign_non_recoverable( message_hash, coincurve_private_key, @@ -85,24 +85,22 @@ def test_signing_is_equal(private_key_bytes, private_key_bytes=private_key_st, message_hash=message_hash_st, direction=st.one_of( - st.just('coincurve-to-native'), - st.just('native-to-coincurve'), + st.just("coincurve-to-native"), + st.just("native-to-coincurve"), ), ) @settings(max_examples=MAX_EXAMPLES) -def test_native_to_coincurve_recover(private_key_bytes, - message_hash, - direction, - native_key_api, - coincurve_key_api): - if direction == 'coincurve-to-native': +def test_native_to_coincurve_recover( + private_key_bytes, message_hash, direction, native_key_api, coincurve_key_api +): + if direction == "coincurve-to-native": backend_a = coincurve_key_api backend_b = native_key_api - elif direction == 'native-to-coincurve': + elif direction == "native-to-coincurve": backend_b = coincurve_key_api backend_a = native_key_api else: - assert False, "invariant" + raise AssertionError("invariant") private_key_a = backend_a.PrivateKey(private_key_bytes) public_key_a = private_key_a.public_key @@ -117,24 +115,22 @@ def test_native_to_coincurve_recover(private_key_bytes, message_hash=message_hash_st, signature_bytes=signature_st, direction=st.one_of( - st.just('coincurve-to-native'), - st.just('native-to-coincurve'), + st.just("coincurve-to-native"), + st.just("native-to-coincurve"), ), ) @settings(max_examples=MAX_EXAMPLES) -def test_coincurve_to_native_invalid_signatures(message_hash, - signature_bytes, - direction, - native_key_api, - coincurve_key_api): - if direction == 'coincurve-to-native': +def test_coincurve_to_native_invalid_signatures( + message_hash, signature_bytes, direction, native_key_api, coincurve_key_api +): + if direction == "coincurve-to-native": backend_a = coincurve_key_api backend_b = native_key_api - elif direction == 'native-to-coincurve': + elif direction == "native-to-coincurve": backend_b = coincurve_key_api backend_a = native_key_api else: - assert False, "invariant" + raise AssertionError("invariant") try: signature_a = backend_a.Signature(signature_bytes) @@ -167,9 +163,9 @@ def test_coincurve_to_native_invalid_signatures(message_hash, @given( private_key_bytes=private_key_st, ) -def test_public_key_compression_is_equal(private_key_bytes, - native_key_api, - coincurve_key_api): +def test_public_key_compression_is_equal( + private_key_bytes, native_key_api, coincurve_key_api +): native_public_key = native_key_api.PrivateKey(private_key_bytes).public_key coincurve_public_key = coincurve_key_api.PrivateKey(private_key_bytes).public_key @@ -182,13 +178,17 @@ def test_public_key_compression_is_equal(private_key_bytes, @given( private_key_bytes=private_key_st, ) -def test_public_key_decompression_is_equal(private_key_bytes, - native_key_api, - coincurve_key_api): +def test_public_key_decompression_is_equal( + private_key_bytes, native_key_api, coincurve_key_api +): public_key_template = coincurve_key_api.PrivateKey(private_key_bytes).public_key compressed_public_key = public_key_template.to_compressed_bytes() - native_public_key = native_key_api.PublicKey.from_compressed_bytes(compressed_public_key) - coincurve_public_key = coincurve_key_api.PublicKey.from_compressed_bytes(compressed_public_key) + native_public_key = native_key_api.PublicKey.from_compressed_bytes( + compressed_public_key + ) + coincurve_public_key = coincurve_key_api.PublicKey.from_compressed_bytes( + compressed_public_key + ) assert native_public_key == coincurve_public_key diff --git a/tests/conftest.py b/tests/conftest.py index 0e9fd82..e167fb4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,13 @@ import pytest - -# Change COLLECT_TYPE_INFO to True here, and run the tests with python2.7 to get a type info dump -# that can later be fed to pyannotate to generate type annotation comments. +# Change COLLECT_TYPE_INFO to True here, and run the tests with python2.7 to get a type +# info dump that can later be fed to pyannotate to generate type annotation comments. COLLECT_TYPE_INFO = False if COLLECT_TYPE_INFO: - from pyannotate_runtime import collect_types + from pyannotate_runtime import ( + collect_types, + ) @pytest.fixture(autouse=True) def collect_types_fixture(): @@ -14,10 +15,8 @@ def collect_types_fixture(): yield collect_types.pause() - def pytest_sessionstart(session): collect_types.init_types_collection() - def pytest_sessionfinish(session, exitstatus): collect_types.dump_stats("type_info.json") diff --git a/tests/core/test_factories.py b/tests/core/test_factories.py index b74f2d2..1855665 100644 --- a/tests/core/test_factories.py +++ b/tests/core/test_factories.py @@ -1,4 +1,6 @@ -from eth_keys import keys +from eth_keys import ( + keys, +) from eth_keys.tools.factories import ( PrivateKeyFactory, PublicKeyFactory, diff --git a/tests/core/test_key_and_signature_datastructures.py b/tests/core/test_key_and_signature_datastructures.py index bfc7dc0..9976a38 100644 --- a/tests/core/test_key_and_signature_datastructures.py +++ b/tests/core/test_key_and_signature_datastructures.py @@ -1,24 +1,26 @@ -from __future__ import unicode_literals - -import pytest - from eth_utils import ( + ValidationError, decode_hex, encode_hex, - keccak, - is_same_address, - is_normalized_address, - is_checksum_address, is_canonical_address, - ValidationError, + is_checksum_address, + is_normalized_address, + is_same_address, + keccak, ) +import pytest -from eth_keys import KeyAPI -from eth_keys.backends import NativeECCBackend -from eth_keys.exceptions import ValidationError as EthKeysValidationErrorCopy - +from eth_keys import ( + KeyAPI, +) +from eth_keys.backends import ( + NativeECCBackend, +) +from eth_keys.exceptions import ( + ValidationError as EthKeysValidationErrorCopy, +) -MSG = b'message' +MSG = b"message" MSGHASH = keccak(MSG) @@ -28,9 +30,9 @@ def key_api(): PK_BYTES = decode_hex( - '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' + "0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d" ) -ADDRESS = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' +ADDRESS = "0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd" @pytest.fixture @@ -129,7 +131,7 @@ def test_to_canonical_address_from_public_key(private_key): def test_hex_conversion(private_key): public_key = private_key.public_key - signature = private_key.sign_msg(b'message') + signature = private_key.sign_msg(b"message") assert hex(public_key) == encode_hex(public_key.to_bytes()) assert hex(private_key) == encode_hex(private_key.to_bytes()) @@ -142,7 +144,7 @@ def test_hex_conversion(private_key): def test_bytes_conversion(key_api, private_key): public_key = private_key.public_key - signature = private_key.sign_msg(b'message') + signature = private_key.sign_msg(b"message") assert public_key.to_bytes() == public_key._raw_key assert private_key.to_bytes() == private_key._raw_key @@ -156,7 +158,9 @@ def test_compressed_bytes_conversion(key_api, private_key): assert key_api.PublicKey.from_compressed_bytes(compressed_bytes) == public_key -@pytest.mark.parametrize('validation_error', (ValidationError, EthKeysValidationErrorCopy)) +@pytest.mark.parametrize( + "validation_error", (ValidationError, EthKeysValidationErrorCopy) +) def test_compressed_bytes_validation(key_api, private_key, validation_error): valid_key = private_key.public_key.to_compressed_bytes() diff --git a/tests/core/test_key_api_proxy_methods.py b/tests/core/test_key_api_proxy_methods.py index 3f9dfd6..dc4cffd 100644 --- a/tests/core/test_key_api_proxy_methods.py +++ b/tests/core/test_key_api_proxy_methods.py @@ -1,17 +1,19 @@ -import pytest - from eth_utils import ( - keccak, ValidationError, + keccak, ) +import pytest -from eth_keys import KeyAPI -from eth_keys.backends import NativeECCBackend - +from eth_keys import ( + KeyAPI, +) +from eth_keys.backends import ( + NativeECCBackend, +) -MSG = b'message' +MSG = b"message" MSGHASH = keccak(MSG) -PK_BYTES = b'\x01' * 32 +PK_BYTES = b"\x01" * 32 @pytest.fixture diff --git a/tests/core/test_keyapi_backend_formats.py b/tests/core/test_keyapi_backend_formats.py index 60f0350..c0ec81e 100644 --- a/tests/core/test_keyapi_backend_formats.py +++ b/tests/core/test_keyapi_backend_formats.py @@ -1,22 +1,26 @@ import pytest -from eth_keys import KeyAPI -from eth_keys.backends import NativeECCBackend +from eth_keys import ( + KeyAPI, +) +from eth_keys.backends import ( + NativeECCBackend, +) @pytest.fixture(autouse=True) def native_backend_env_var(monkeypatch): - monkeypatch.setenv('ECC_BACKEND_CLASS', 'eth_keys.backends.native.NativeECCBackend') + monkeypatch.setenv("ECC_BACKEND_CLASS", "eth_keys.backends.native.NativeECCBackend") @pytest.mark.parametrize( - 'backend', + "backend", ( None, NativeECCBackend(), NativeECCBackend, - 'eth_keys.backends.NativeECCBackend', - 'eth_keys.backends.native.NativeECCBackend', + "eth_keys.backends.NativeECCBackend", + "eth_keys.backends.native.NativeECCBackend", ), ) def test_supported_backend_formats(backend): diff --git a/tests/core/test_utils_der.py b/tests/core/test_utils_der.py index 297925b..0afe8ee 100644 --- a/tests/core/test_utils_der.py +++ b/tests/core/test_utils_der.py @@ -1,21 +1,19 @@ -import itertools -import pytest - import asn1tools from hypothesis import ( example, + given, settings, strategies as st, - given, ) from pyasn1.codec.der import ( - encoder as pyasn1_encoder, decoder as pyasn1_decoder, + encoder as pyasn1_encoder, ) from pyasn1.type import ( - univ, namedtype, + univ, ) +import pytest from eth_keys.utils.der import ( two_int_sequence_decoder, @@ -44,8 +42,8 @@ def asn1tools_decode(encoded): class TwoInts(univ.Sequence): componentType = namedtype.NamedTypes( - namedtype.NamedType('r', univ.Integer()), - namedtype.NamedType('s', univ.Integer()), + namedtype.NamedType("r", univ.Integer()), + namedtype.NamedType("s", univ.Integer()), ) @@ -61,11 +59,12 @@ def pyasn1_decode(encoded): return decoded[0]["r"], decoded[0]["s"] -MAX_32_BYTE_INT = 256 ** 32 - 1 +MAX_32_BYTE_INT = 256**32 - 1 uint32strategy = st.integers(min_value=0, max_value=MAX_32_BYTE_INT) + @pytest.mark.parametrize( - 'encoder, decoder', + "encoder, decoder", ( (two_int_sequence_encoder, asn1tools_decode), (two_int_sequence_encoder, pyasn1_decode), @@ -74,11 +73,11 @@ def pyasn1_decode(encoded): (pyasn1_encode, two_int_sequence_decoder), ), ids=( - 'local_encode=>asn1tools_decode', - 'local_encode=>pyasn1_decode', - 'local_encode=>local_decode', - 'asn1tools_encode=>local_decode', - 'pyasn1_encode=>local_decode', + "local_encode=>asn1tools_decode", + "local_encode=>pyasn1_decode", + "local_encode=>local_decode", + "asn1tools_encode=>local_decode", + "pyasn1_encode=>local_decode", ), ) @given( diff --git a/tox.ini b/tox.ini index b27b4b7..6c528ae 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,9 @@ [tox] envlist= - py{38,39,310,311}-backends-coincurve{7,8,9,10,11,12,13,14,15} + py38-backends-coincurve{12,13,14} + py{38,39}-backends-coincurve15 + py{38,39,10}-backends-coincurve{16,17} + py{38,39,10,11}-backends-coincurve18 py{38,39,310,311}-core py{38,39,310,311}-lint py{38,39,310,311}-wheel @@ -18,7 +21,7 @@ usedevelop=True commands= core: pytest {posargs:tests/core} backends: pytest {posargs:tests/backends} - docs: make check-docs + docs: make docs basepython= docs: python py38: python3.8 @@ -26,15 +29,13 @@ basepython= py310: python3.10 py311: python3.11 deps= .[test] - coincurve7: coincurve>=7.0.0,<8.0.0 - coincurve8: coincurve>=8.0.0,<9.0.0 - coincurve9: coincurve>=9.0.0,<10.0.0 - coincurve10: coincurve>=10.0.0,<11.0.0 - coincurve11: coincurve>=11.0.0,<12.0.0 coincurve12: coincurve>=12.0.0,<13.0.0 coincurve13: coincurve>=13.0.0,<14.0.0 coincurve14: coincurve>=14.0.0,<15.0.0 coincurve15: coincurve>=15.0.0,<16.0.0 + coincurve16: coincurve>=16.0.0,<17.0.0 + coincurve17: coincurve>=17.0.0,<18.0.0 + coincurve18: coincurve>=18.0.0,<19.0.0 setenv= backends: REQUIRE_COINCURVE=True extras=