From 319d0f16e33892d0f9c5fc4fd22186bf5e75acb8 Mon Sep 17 00:00:00 2001 From: grdddj Date: Mon, 14 Feb 2022 10:42:06 +0100 Subject: [PATCH] chore(python): loosen ethereum dependencies [no changelog] --- poetry.lock | 11 ++++++ pyproject.toml | 1 + python/requirements-optional.txt | 2 +- python/requirements.txt | 1 + python/setup.cfg | 2 +- python/setup.py | 3 +- python/src/trezorlib/cli/ethereum.py | 56 +++++++++++++++------------- python/tox.ini | 7 ++++ 8 files changed, 54 insertions(+), 29 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5bde0ad09..399892b1f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -789,6 +789,14 @@ colorama = "*" dev = ["black", "flake8", "isort"] tests = ["pytest"] +[[package]] +name = "simple-rlp" +version = "0.1.2" +description = "RLP (Recursive Length Prefix) - Encode and decode data structures" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "six" version = "1.16.0" @@ -1567,6 +1575,9 @@ shamir-mnemonic = [ {file = "shamir-mnemonic-0.2.2.tar.gz", hash = "sha256:7fb9b592e5c518192c0b0caa2c2d82e342fddd186693bc64be9647eace1b9182"}, {file = "shamir_mnemonic-0.2.2-py3-none-any.whl", hash = "sha256:7d9facea70379cad02bab18d4572c0fcd033c9d7effe5da095b9e0944bf5fbbf"}, ] +simple-rlp = [ + {file = "simple-rlp-0.1.2.tar.gz", hash = "sha256:5c4a9c58f1b742f7fa8af0fe4ea6ff9fb02294ae041912f771570dfaf339d2b9"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index bb0c6b15c..d1d1af5f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ ed25519 = "^1.4" requests = "^2.19" termcolor = "*" Pillow = "^9" +simple-rlp = "^0.1.2" # crypto ecdsa = "^0.16" diff --git a/python/requirements-optional.txt b/python/requirements-optional.txt index 3b32873ba..b406cde0d 100644 --- a/python/requirements-optional.txt +++ b/python/requirements-optional.txt @@ -1,6 +1,6 @@ hidapi >= 0.7.99.post20 -rlp >= 1.1.0 web3 >= 4.8 Pillow stellar-sdk>=4.0.0,<6.0.0 types-click +rlp>=1.1.0 ; python_version<'3.7' diff --git a/python/requirements.txt b/python/requirements.txt index e0d1dd4b2..678d3afe9 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -6,3 +6,4 @@ libusb1>=1.6.4 construct>=2.9,!=2.10.55 typing_extensions>=3.10 dataclasses ; python_version<'3.7' +simple-rlp>=0.1.2 ; python_version>='3.7' diff --git a/python/setup.cfg b/python/setup.cfg index 426f6fdca..76e8589ba 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -25,7 +25,7 @@ per-file-ignores = helper-scripts/*:I tools/*:I tests/*:I -known-modules = libusb1:[usb1],hidapi:[hid],PyQt5:[PyQt5.QtWidgets,PyQt5.QtGui,PyQt5.QtCore] +known-modules = libusb1:[usb1],hidapi:[hid],PyQt5:[PyQt5.QtWidgets,PyQt5.QtGui,PyQt5.QtCore],simple-rlp:[rlp] [isort] multi_line_output = 3 diff --git a/python/setup.py b/python/setup.py index 1086caa84..a364f5de9 100755 --- a/python/setup.py +++ b/python/setup.py @@ -31,11 +31,12 @@ install_requires = [ "construct>=2.9", "typing_extensions>=3.10", "dataclasses ; python_version<'3.7'", + "simple-rlp>=0.1.2 ; python_version>='3.7'", ] extras_require = { "hidapi": ["hidapi>=0.7.99.post20"], - "ethereum": ["rlp>=1.1.0", "web3>=4.8"], + "ethereum": ["rlp>=1.1.0 ; python_version<'3.7'", "web3>=4.8"], "qt-widgets": ["PyQt5"], "extra": ["Pillow"], "stellar": ["stellar-sdk>=4.0.0,<6.0.0"], diff --git a/python/src/trezorlib/cli/ethereum.py b/python/src/trezorlib/cli/ethereum.py index 61c5d3ddb..a435c553c 100644 --- a/python/src/trezorlib/cli/ethereum.py +++ b/python/src/trezorlib/cli/ethereum.py @@ -21,21 +21,14 @@ from decimal import Decimal from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, TextIO, Tuple import click +import rlp from .. import ethereum, tools from . import with_client if TYPE_CHECKING: - from ..client import TrezorClient - -try: - import rlp import web3 - - HAVE_SIGN_TX = True -except Exception: - HAVE_SIGN_TX = False - + from ..client import TrezorClient PATH_HELP = "BIP-32 path, e.g. m/44'/60'/0'/0/0" @@ -63,6 +56,26 @@ ETHER_UNITS = { } # fmt: on +# So that we can import the web3 library only when really used and reuse the instance +_WEB3_INSTANCE: Optional["web3.Web3"] = None + + +def _get_web3() -> "web3.Web3": + global _WEB3_INSTANCE + if _WEB3_INSTANCE is None: + try: + import web3 + + _WEB3_INSTANCE = web3.Web3() + except ModuleNotFoundError: + click.echo("Ethereum requirements not installed.") + click.echo("Please run:") + click.echo() + click.echo(" pip install web3") + sys.exit(1) + + return _WEB3_INSTANCE + def _amount_to_int( ctx: click.Context, param: Any, value: Optional[str] @@ -113,9 +126,7 @@ def _list_units(ctx: click.Context, param: Any, value: bool) -> None: ctx.exit() -def _erc20_contract( - w3: "web3.Web3", token_address: str, to_address: str, amount: int -) -> str: +def _erc20_contract(token_address: str, to_address: str, amount: int) -> str: min_abi = [ { "name": "transfer", @@ -128,7 +139,7 @@ def _erc20_contract( "outputs": [{"name": "", "type": "bool"}], } ] - contract = w3.eth.contract(address=token_address, abi=min_abi) # type: ignore ["str" cannot be assigned to type "Address | ChecksumAddress | ENS"] + contract = _get_web3().eth.contract(address=token_address, abi=min_abi) # type: ignore ["str" cannot be assigned to type "Address | ChecksumAddress | ENS"] return contract.encodeABI("transfer", [to_address, amount]) @@ -260,20 +271,13 @@ def sign_tx( try to connect to an ethereum node and auto-fill these values. You can configure the connection with WEB3_PROVIDER_URI environment variable. """ - if not HAVE_SIGN_TX: - click.echo("Ethereum requirements not installed.") - click.echo("Please run:") - click.echo() - click.echo(" pip install web3 rlp") - sys.exit(1) is_eip1559 = eip2718_type == 2 - w3 = web3.Web3() if ( (not is_eip1559 and gas_price is None) or any(x is None for x in (gas_limit, nonce)) or publish - ) and not w3.isConnected(): + ) and not _get_web3().isConnected(): click.echo("Failed to connect to Ethereum node.") click.echo( "If you want to sign offline, make sure you provide --gas-price, " @@ -289,7 +293,7 @@ def sign_tx( from_address = ethereum.get_address(client, address_n) if token: - data = _erc20_contract(w3, token, to_address, amount) + data = _erc20_contract(token, to_address, amount) to_address = token amount = 0 @@ -299,7 +303,7 @@ def sign_tx( data_bytes = b"" if gas_limit is None: - gas_limit = w3.eth.estimateGas( + gas_limit = _get_web3().eth.estimateGas( { "to": to_address, "from": from_address, @@ -309,7 +313,7 @@ def sign_tx( ) if nonce is None: - nonce = w3.eth.getTransactionCount(from_address) + nonce = _get_web3().eth.getTransactionCount(from_address) assert gas_limit is not None assert nonce is not None @@ -332,7 +336,7 @@ def sign_tx( ) else: if gas_price is None: - gas_price = w3.eth.gasPrice + gas_price = _get_web3().eth.gasPrice assert gas_price is not None sig = ethereum.sign_tx( client, @@ -378,7 +382,7 @@ def sign_tx( tx_hex = f"0x{eip2718_prefix}{transaction.hex()}" if publish: - tx_hash = w3.eth.sendRawTransaction(tx_hex).hex() + tx_hash = _get_web3().eth.sendRawTransaction(tx_hex).hex() return f"Transaction published with ID: {tx_hash}" else: return f"Signed raw transaction:\n{tx_hex}" diff --git a/python/tox.ini b/python/tox.ini index aec691fc7..02cb4288b 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -1,3 +1,10 @@ +# NOTE: for running the tests locally in `nix-shell`, it is necessary +# to spawn the `nix-shell` with `fullDeps` argument, so the command is: +# `nix-shell --arg fullDeps true` +# This will make sure all the python versions are installed. +# (it could be beneficial to comment out `bitcoind` in `shell.nix` locally before running it, +# as building that from source takes a very long time) + [tox] envlist = py36,