diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ca76b3632..2def3efa4c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,6 +11,10 @@ parameters: type: string common: &common + parameters: + python_exec: + type: string + default: "python" working_directory: ~/repo steps: - checkout @@ -28,6 +32,38 @@ common: &common - restore_cache: keys: - cache-v1-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} + - run: + name: install pypy3 if python_exec is pypy3 + command: | + if [ "<< parameters.python_exec >>" == "pypy3" ]; then + sudo apt-get update + + # If .pyenv already exists, remove and reinstall to get latest version + if [ -d "$HOME/.pyenv" ]; then + echo "Removing existing .pyenv directory..." + rm -rf $HOME/.pyenv + fi + curl https://pyenv.run | bash + export PATH="$HOME/.pyenv/bin:$PATH" + eval "$(pyenv init --path)" + eval "$(pyenv init -)" + eval "$(pyenv virtualenv-init -)" + + # Find the latest PyPy version matching the python minor version + latest_pypy_version=$(pyenv install --list | grep -E "pypy3\.<< parameters.python_minor_version >>" | grep -v "\-src" | tail -1 | tr -d ' ') + echo "Latest PyPy version: $latest_pypy_version" + + # Install the latest PyPy 3.10 version using pyenv if not already installed + pyenv install "$latest_pypy_version" + pyenv global "$latest_pypy_version" + + # Verify the correct PyPy version is being used + pypy3 --version + + # Install pip using the newly installed PyPy version + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + pypy3 get-pip.py + fi - run: name: install dependencies command: | @@ -36,7 +72,7 @@ common: &common python web3/scripts/install_pre_releases.py - run: name: run tox - command: python -m tox run -r + command: << parameters.python_exec >> -m tox -r - save_cache: paths: - .hypothesis @@ -145,12 +181,30 @@ jobs: type: string tox_env: type: string + python_exec: + type: string + default: "python" <<: *common docker: - image: cimg/python:3.<< parameters.python_minor_version >> environment: TOXENV: py3<< parameters.python_minor_version >>-<< parameters.tox_env >> + common-pypy: + parameters: + python_minor_version: + type: string + tox_env: + type: string + python_exec: + type: string + default: "pypy3" + <<: *common + docker: + - image: cimg/python:3.<< parameters.python_minor_version >> + environment: + TOXENV: pypy3<< parameters.python_minor_version >>-<< parameters.tox_env >> + geth: parameters: python_minor_version: @@ -228,13 +282,44 @@ workflows: python_minor_version: ["8", "9", "10", "11", "12"] tox_env: [ "lint", - "core", - "core_async", - "ens", + "core-pyevm", + "core-pyevm_async", + "ens-pyevm", "ensip15", "wheel" ] + python_exec: "python" name: "py3<< matrix.python_minor_version >>-<< matrix.tox_env >>" + - common: + matrix: + parameters: + # eels only supports 3.10 and above + python_minor_version: ["10", "11", "12"] + tox_env: [ + "core-eels", + "core-eels_async", + "ens-eels", + "integration-ethtester-eels" + ] + python_exec: "python" + name: "py3<< matrix.python_minor_version >>-<< matrix.tox_env >>" + - common-pypy: + matrix: + parameters: + # eels only supports 3.10 and above; pyenv only has pypy3.10 available + python_minor_version: ["10"] + tox_env: [ + "core-eels", + "core-eels_async", + "ens-eels", + "integration-ethtester-eels", + "core-pyevm", + "core-pyevm_async", + "ens-pyevm", + "integration-ethtester-pyevm" + ] + python_exec: "pypy3" + name: "pypy3<< matrix.python_minor_version >>-<< matrix.tox_env >>" - geth: matrix: parameters: @@ -246,7 +331,7 @@ workflows: "integration-goethereum-http_async", "integration-goethereum-legacy_ws", "integration-goethereum-ws", - "integration-ethtester" + "integration-ethtester-pyevm" ] name: "py3<< matrix.python_minor_version >>-<< matrix.tox_env >>" - docs: @@ -260,7 +345,6 @@ workflows: python_minor_version: ["10", "11", "12"] name: "py3<< matrix.python_minor_version >>-windows-wheel" - nightly: triggers: - schedule: diff --git a/conftest.py b/conftest.py index 06cbbdcacc..c3d8e7d64f 100644 --- a/conftest.py +++ b/conftest.py @@ -2,6 +2,9 @@ import time import warnings +from eth_tester import ( + EthereumTester, +) import pytest_asyncio from tests.utils import ( @@ -71,15 +74,15 @@ def _wait_for_transaction(w3, txn_hash, timeout=120): @pytest.fixture -def w3(): - w3 = Web3(EthereumTesterProvider()) +def w3(backend_class): + w3 = Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) w3.eth.default_account = w3.eth.accounts[0] return w3 @pytest.fixture(scope="module") -def w3_non_strict_abi(): - w3 = Web3(EthereumTesterProvider()) +def w3_non_strict_abi(backend_class): + w3 = Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) w3.eth.default_account = w3.eth.accounts[0] w3.strict_bytes_type_checking = False return w3 diff --git a/docs/contributing.rst b/docs/contributing.rst index b45302b4c4..aa4ae8fb53 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -160,11 +160,12 @@ Typically, you'll just want to run a subset instead, like: $ pytest tests/core/eth-module/test_accounts.py -You can use ``tox`` to run all the tests for a given version of Python: +You can use ``tox`` to run all the core tests for a given version of Python. You must +an available backend for eth-tester to use (currently {'pyevm', 'eels'}). .. code:: sh - $ tox -e py38-core + $ tox -e py38-core --backend=pyevm Linting is also performed by the CI. You can save yourself some time by checking for diff --git a/setup.py b/setup.py index 48a3b5352c..23cf7781c9 100644 --- a/setup.py +++ b/setup.py @@ -4,11 +4,15 @@ setup, ) +CUSTOM_ETH_TESTER_BRANCH = " @ git+https://github.com/fselmo/eth-tester@eels-backend" + extras_require = { "tester": [ # Note: ethereum-maintained libraries in this list should be added to the # `install_pre_releases.py` script. - "eth-tester[py-evm]>=0.11.0b1,<0.13.0b1", + f"eth-tester[py-evm]{CUSTOM_ETH_TESTER_BRANCH}", + # if python version >= 3.10, install the eels backend: + f"eth-tester[eels]{CUSTOM_ETH_TESTER_BRANCH} ; python_version >= '3.10'", "py-geth>=5.0.0", ], "dev": [ diff --git a/tests/conftest.py b/tests/conftest.py index 33cf0f69a5..7a079e7906 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,9 @@ Type, ) +from eth_tester import ( + PyEVMBackend, +) from eth_utils import ( event_signature_to_log_topic, to_bytes, @@ -23,13 +26,15 @@ get_open_port, ) +SUPPORTED_ETH_TESTER_BACKENDS = {"pyevm", "eels"} + @pytest.fixture(scope="module", params=[lambda x: to_bytes(hexstr=x), identity]) def address_conversion_func(request): return request.param -@pytest.fixture() +@pytest.fixture def open_port(): return get_open_port() @@ -37,6 +42,47 @@ def open_port(): # --- session-scoped constants --- # +def pytest_addoption(parser): + parser.addoption( + "--backend", + action="store", + default=None, + help="Specify the backend for `EthereumTester` to use.", + ) + + +def pytest_collection_modifyitems(config, items): + backend_required_for_tests = any( + "backend_class" in item.fixturenames for item in items + ) + if backend_required_for_tests: + backend = config.getoption("--backend") + if not backend: + raise pytest.UsageError( + "This test run requires a specified a backend via the `--backend` " + "command line option. Supported backends are: " + f"{SUPPORTED_ETH_TESTER_BACKENDS}" + ) + elif backend not in SUPPORTED_ETH_TESTER_BACKENDS: + raise pytest.UsageError(f"Unsupported backend: `{backend}`.") + + +@pytest.fixture(scope="session") +def backend_class(request): + backend = request.config.getoption("--backend") + if backend == "pyevm": + return PyEVMBackend + elif backend == "eels": + # conditionally import since eels is only supported on python >= 3.10 + from eth_tester.backends.eels import ( + EELSBackend, + ) + + return EELSBackend + else: + raise ValueError("Invariant: Unreachable code path.") + + @pytest.fixture(scope="session") def emitter_contract_data(): return EMITTER_CONTRACT_DATA diff --git a/tests/core/conftest.py b/tests/core/conftest.py index 4210a1245c..8497aa1a03 100644 --- a/tests/core/conftest.py +++ b/tests/core/conftest.py @@ -1,5 +1,8 @@ import pytest +from eth_tester import ( + EthereumTester, +) import pytest_asyncio from web3 import ( @@ -117,16 +120,16 @@ def __init__(self, a, b): @pytest_asyncio.fixture -async def async_w3(): - w3 = AsyncWeb3(AsyncEthereumTesterProvider()) +async def async_w3(backend_class): + w3 = AsyncWeb3(AsyncEthereumTesterProvider(EthereumTester(backend=backend_class()))) accounts = await w3.eth.accounts w3.eth.default_account = accounts[0] return w3 @pytest_asyncio.fixture -async def async_w3_non_strict_abi(): - w3 = AsyncWeb3(AsyncEthereumTesterProvider()) +async def async_w3_non_strict_abi(backend_class): + w3 = AsyncWeb3(AsyncEthereumTesterProvider(EthereumTester(backend=backend_class()))) w3.strict_bytes_type_checking = False accounts = await w3.eth.accounts w3.eth.default_account = accounts[0] diff --git a/tests/core/contracts/test_contract_attributes.py b/tests/core/contracts/test_contract_attributes.py index bb36550f48..9d40248814 100644 --- a/tests/core/contracts/test_contract_attributes.py +++ b/tests/core/contracts/test_contract_attributes.py @@ -6,7 +6,7 @@ ) -@pytest.fixture() +@pytest.fixture def abi(): return """[{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"function"}, {"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]""" # noqa: E501 diff --git a/tests/core/contracts/test_contract_build_transaction.py b/tests/core/contracts/test_contract_build_transaction.py index a49d60effc..f00768b202 100644 --- a/tests/core/contracts/test_contract_build_transaction.py +++ b/tests/core/contracts/test_contract_build_transaction.py @@ -22,7 +22,7 @@ def test_build_transaction_not_paying_to_nonpayable_function( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -49,7 +49,7 @@ def test_build_transaction_with_contract_no_arguments( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -63,7 +63,7 @@ def test_build_transaction_with_contract_no_arguments_no_parens( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -77,7 +77,7 @@ def test_build_transaction_with_contract_fallback_function( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -95,7 +95,7 @@ def test_build_transaction_with_contract_class_method( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -111,7 +111,7 @@ def test_build_transaction_with_contract_default_account_is_set( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -130,7 +130,7 @@ def my_gas_price_strategy(w3, transaction_params): "data": "0x5b34b966", "value": 0, "gasPrice": 5, - "chainId": 131277322940537, + "chainId": w3.eth.chain_id, } @@ -168,7 +168,6 @@ def test_build_transaction_with_contract_to_address_supplied_errors( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), @@ -181,32 +180,29 @@ def test_build_transaction_with_contract_to_address_supplied_errors( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), ( # legacy transaction, explicit gasPrice - {"gasPrice": 22**8}, + {"gasPrice": 22 * 10**8}, (5,), {}, { "data": "0x6abbb3b40000000000000000000000000000000000000000000000000000000000000005", # noqa: E501 "value": 0, - "gasPrice": 22**8, - "chainId": 131277322940537, + "gasPrice": 22 * 10**8, }, False, ), ( - {"maxFeePerGas": 22**8, "maxPriorityFeePerGas": 22**8}, + {"maxFeePerGas": 22 * 10**8, "maxPriorityFeePerGas": 22 * 10**8}, (5,), {}, { "data": "0x6abbb3b40000000000000000000000000000000000000000000000000000000000000005", # noqa: E501 "value": 0, - "maxFeePerGas": 22**8, - "maxPriorityFeePerGas": 22**8, - "chainId": 131277322940537, + "maxFeePerGas": 22 * 10**8, + "maxPriorityFeePerGas": 22 * 10**8, }, False, ), @@ -220,7 +216,6 @@ def test_build_transaction_with_contract_to_address_supplied_errors( "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, "nonce": 7, - "chainId": 131277322940537, }, True, ), @@ -233,7 +228,6 @@ def test_build_transaction_with_contract_to_address_supplied_errors( "value": 20000, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), @@ -261,6 +255,7 @@ def test_build_transaction_with_contract_arguments( if skip_testrpc: skip_if_testrpc(w3) + expected["chainId"] = w3.eth.chain_id txn = build_transaction( contract=math_contract, contract_function="incrementCounter", @@ -290,7 +285,7 @@ async def test_async_build_transaction_not_paying_to_nonpayable_function( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -319,7 +314,7 @@ async def test_async_build_transaction_with_contract_no_arguments( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -334,7 +329,7 @@ async def test_async_build_transaction_with_contract_no_arguments_no_parens( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -349,7 +344,7 @@ async def test_async_build_transaction_with_contract_fallback_function( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -371,7 +366,7 @@ async def test_async_build_transaction_with_contract_class_method( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -388,7 +383,7 @@ async def test_async_build_transaction_with_contract_default_account_is_set( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 10**9, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -408,7 +403,7 @@ def my_gas_price_strategy(async_w3, transaction_params): "data": "0x5b34b966", "value": 0, "gasPrice": 5, - "chainId": 131277322940537, + "chainId": await async_w3.eth.chain_id, } @@ -449,7 +444,6 @@ async def test_async_build_transaction_with_contract_to_address_supplied_errors( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), @@ -462,32 +456,29 @@ async def test_async_build_transaction_with_contract_to_address_supplied_errors( "value": 0, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), ( # legacy transaction, explicit gasPrice - {"gasPrice": 22**8}, + {"gasPrice": 22 * 10**8}, (5,), {}, { "data": "0x6abbb3b40000000000000000000000000000000000000000000000000000000000000005", # noqa: E501 "value": 0, - "gasPrice": 22**8, - "chainId": 131277322940537, + "gasPrice": 22 * 10**8, }, False, ), ( - {"maxFeePerGas": 22**8, "maxPriorityFeePerGas": 22**8}, + {"maxFeePerGas": 22 * 10**8, "maxPriorityFeePerGas": 22 * 10**8}, (5,), {}, { "data": "0x6abbb3b40000000000000000000000000000000000000000000000000000000000000005", # noqa: E501 "value": 0, - "maxFeePerGas": 22**8, - "maxPriorityFeePerGas": 22**8, - "chainId": 131277322940537, + "maxFeePerGas": 22 * 10**8, + "maxPriorityFeePerGas": 22 * 10**8, }, False, ), @@ -501,7 +492,6 @@ async def test_async_build_transaction_with_contract_to_address_supplied_errors( "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, "nonce": 7, - "chainId": 131277322940537, }, True, ), @@ -514,7 +504,6 @@ async def test_async_build_transaction_with_contract_to_address_supplied_errors( "value": 20000, "maxFeePerGas": 2750000000, "maxPriorityFeePerGas": 1000000000, - "chainId": 131277322940537, }, False, ), @@ -542,6 +531,7 @@ async def test_async_build_transaction_with_contract_with_arguments( if skip_testrpc: async_skip_if_testrpc(async_w3) + expected["chainId"] = await async_w3.eth.chain_id txn = await async_build_transaction( contract=async_math_contract, contract_function="incrementCounter", diff --git a/tests/core/contracts/test_contract_caller_interface.py b/tests/core/contracts/test_contract_caller_interface.py index 0c3df9ce90..81b85972eb 100644 --- a/tests/core/contracts/test_contract_caller_interface.py +++ b/tests/core/contracts/test_contract_caller_interface.py @@ -8,12 +8,12 @@ ) -@pytest.fixture() +@pytest.fixture def address(w3): return w3.eth.accounts[1] -@pytest.fixture() +@pytest.fixture def transaction_dict(w3, address): return { "from": address, diff --git a/tests/core/contracts/test_contract_estimate_gas.py b/tests/core/contracts/test_contract_estimate_gas.py index 3b8e784297..bf6209d7c4 100644 --- a/tests/core/contracts/test_contract_estimate_gas.py +++ b/tests/core/contracts/test_contract_estimate_gas.py @@ -87,10 +87,9 @@ def test_estimate_gas_sending_ether_to_nonpayable_function( def test_estimate_gas_accepts_latest_block(w3, math_contract, transact): - gas_estimate = math_contract.functions.counter().estimate_gas( + gas_estimate = math_contract.functions.incrementCounter().estimate_gas( block_identifier="latest" ) - txn_hash = transact(contract=math_contract, contract_function="incrementCounter") txn_receipt = w3.eth.wait_for_transaction_receipt(txn_hash) @@ -196,7 +195,7 @@ async def test_async_estimate_gas_sending_ether_to_nonpayable_function( async def test_async_estimate_gas_accepts_latest_block( async_w3, async_math_contract, async_transact ): - gas_estimate = await async_math_contract.functions.counter().estimate_gas( + gas_estimate = await async_math_contract.functions.incrementCounter().estimate_gas( block_identifier="latest" ) diff --git a/tests/core/contracts/test_contract_example.py b/tests/core/contracts/test_contract_example.py index e47c9352d9..9eb9a1e7de 100644 --- a/tests/core/contracts/test_contract_example.py +++ b/tests/core/contracts/test_contract_example.py @@ -2,6 +2,9 @@ # of how to write unit tests with web3.py import pytest +from eth_tester import ( + EthereumTester, +) import pytest_asyncio from web3 import ( @@ -15,8 +18,8 @@ @pytest.fixture -def tester_provider(): - return EthereumTesterProvider() +def tester_provider(backend_class): + return EthereumTesterProvider(EthereumTester(backend=backend_class())) @pytest.fixture @@ -118,8 +121,10 @@ def async_eth_tester(): @pytest_asyncio.fixture() -async def async_w3(): - async_w3 = AsyncWeb3(AsyncEthereumTesterProvider()) +async def async_w3(backend_class): + async_w3 = AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) accounts = await async_w3.eth.accounts async_w3.eth.default_account = accounts[0] return async_w3 diff --git a/tests/core/contracts/test_contract_init.py b/tests/core/contracts/test_contract_init.py index 57f69be353..d6a9f8bc5f 100644 --- a/tests/core/contracts/test_contract_init.py +++ b/tests/core/contracts/test_contract_init.py @@ -10,7 +10,7 @@ ) -@pytest.fixture() +@pytest.fixture def math_addr(math_contract_factory, address_conversion_func): w3 = math_contract_factory.w3 deploy_txn = math_contract_factory.constructor().transact( diff --git a/tests/core/contracts/test_extracting_event_data.py b/tests/core/contracts/test_extracting_event_data.py index cfb3338ad4..5199db569a 100644 --- a/tests/core/contracts/test_extracting_event_data.py +++ b/tests/core/contracts/test_extracting_event_data.py @@ -28,7 +28,7 @@ ) -@pytest.fixture() +@pytest.fixture def dup_txn_receipt(w3, indexed_event_contract, wait_for_transaction, event_contract): emitter_fn = indexed_event_contract.functions.logTwoEvents diff --git a/tests/core/contracts/test_extracting_event_data_old.py b/tests/core/contracts/test_extracting_event_data_old.py index 08b1a607d5..f5f817267e 100644 --- a/tests/core/contracts/test_extracting_event_data_old.py +++ b/tests/core/contracts/test_extracting_event_data_old.py @@ -9,7 +9,7 @@ ) -@pytest.fixture() +@pytest.fixture def emitter( w3, emitter_contract_data, diff --git a/tests/core/eth-module/test_accounts.py b/tests/core/eth-module/test_accounts.py index 6f935acf4e..78cf51adc6 100644 --- a/tests/core/eth-module/test_accounts.py +++ b/tests/core/eth-module/test_accounts.py @@ -9,6 +9,9 @@ from eth_account.signers.local import ( LocalAccount, ) +from eth_tester import ( + EthereumTester, +) from eth_utils import ( is_bytes, is_checksum_address, @@ -98,9 +101,9 @@ def acct(request, w3): raise Exception("Unreachable!") -@pytest.fixture() -def w3(): - return Web3(EthereumTesterProvider()) +@pytest.fixture +def w3(backend_class): + return Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) def test_eth_default_account_is_empty_by_default(w3): @@ -560,9 +563,11 @@ def test_eth_account_sign_and_send_EIP155_transaction_to_eth_tester( # -- async -- # -@pytest.fixture() -def async_w3(): - return AsyncWeb3(AsyncEthereumTesterProvider()) +@pytest.fixture +def async_w3(backend_class): + return AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) @patch("web3.eth.BaseEth.account", "wired via BaseEth") diff --git a/tests/core/eth-module/test_eth_filter.py b/tests/core/eth-module/test_eth_filter.py index beac58d431..427e43fb95 100644 --- a/tests/core/eth-module/test_eth_filter.py +++ b/tests/core/eth-module/test_eth_filter.py @@ -1,5 +1,8 @@ import pytest +from eth_tester import ( + EthereumTester, +) import pytest_asyncio from web3 import ( @@ -30,11 +33,11 @@ def test_eth_filter_creates_correct_filter_type(w3): # --- async --- # -@pytest_asyncio.fixture() -async def async_w3(): - provider = AsyncEthereumTesterProvider() - w3 = AsyncWeb3(provider) - return w3 +@pytest_asyncio.fixture +async def async_w3(backend_class): + return AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) @pytest.mark.asyncio diff --git a/tests/core/eth-module/test_eth_properties.py b/tests/core/eth-module/test_eth_properties.py index 39333d6045..25a12bd6c4 100644 --- a/tests/core/eth-module/test_eth_properties.py +++ b/tests/core/eth-module/test_eth_properties.py @@ -1,5 +1,9 @@ import pytest +from eth_tester import ( + EthereumTester, +) + from web3 import ( AsyncWeb3, ) @@ -9,18 +13,19 @@ @pytest.fixture -def async_w3(): +def async_w3(backend_class): return AsyncWeb3( - AsyncEthereumTesterProvider(), + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())), ) def test_eth_chain_id(w3): - assert w3.eth.chain_id == 131277322940537 # from fixture generation file + assert w3.eth.chain_id == w3.provider.ethereum_tester.backend.chain.chain_id @pytest.mark.asyncio async def test_async_eth_chain_id(async_w3): assert ( - await async_w3.eth.chain_id == 131277322940537 - ) # from fixture generation file + await async_w3.eth.chain_id + == async_w3.provider.ethereum_tester.backend.chain.chain_id + ) diff --git a/tests/core/filtering/conftest.py b/tests/core/filtering/conftest.py index 14d16f1a99..2dd5f6790e 100644 --- a/tests/core/filtering/conftest.py +++ b/tests/core/filtering/conftest.py @@ -22,8 +22,8 @@ params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -def w3(request): - return _w3_fixture_logic(request) +def w3(request, backend_class): + return _w3_fixture_logic(request, backend_class) @pytest.fixture @@ -69,8 +69,8 @@ def create_filter(request): params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -async def async_w3(request): - return await _async_w3_fixture_logic(request) +async def async_w3(request, backend_class): + return await _async_w3_fixture_logic(request, backend_class) @pytest.fixture diff --git a/tests/core/filtering/test_contract_data_filters.py b/tests/core/filtering/test_contract_data_filters.py index 4729b3b204..77d9005761 100644 --- a/tests/core/filtering/test_contract_data_filters.py +++ b/tests/core/filtering/test_contract_data_filters.py @@ -81,8 +81,8 @@ def array_values(draw): params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -def w3(request): - return _w3_fixture_logic(request) +def w3(request, backend_class): + return _w3_fixture_logic(request, backend_class) @pytest.fixture(scope="module") @@ -284,8 +284,8 @@ def event_loop(): params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -async def async_w3(request): - return await _async_w3_fixture_logic(request) +async def async_w3(request, backend_class): + return await _async_w3_fixture_logic(request, backend_class) @pytest.fixture(scope="module") diff --git a/tests/core/filtering/test_contract_topic_filters.py b/tests/core/filtering/test_contract_topic_filters.py index 019147e0bf..3ba830e37b 100644 --- a/tests/core/filtering/test_contract_topic_filters.py +++ b/tests/core/filtering/test_contract_topic_filters.py @@ -81,8 +81,8 @@ def array_values(draw): params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -def w3(request): - return _w3_fixture_logic(request) +def w3(request, backend_class): + return _w3_fixture_logic(request, backend_class) @pytest.fixture(scope="module") @@ -265,8 +265,8 @@ def event_loop(): params=[True, False], ids=["LocalFilterMiddleware", "node_based_filter"], ) -async def async_w3(request): - return await _async_w3_fixture_logic(request) +async def async_w3(request, backend_class): + return await _async_w3_fixture_logic(request, backend_class) @pytest_asyncio.fixture(scope="module") diff --git a/tests/core/filtering/test_existing_filter_instance.py b/tests/core/filtering/test_existing_filter_instance.py index 8ae2cdd2ee..ccf97be687 100644 --- a/tests/core/filtering/test_existing_filter_instance.py +++ b/tests/core/filtering/test_existing_filter_instance.py @@ -7,7 +7,7 @@ ) -@pytest.fixture() +@pytest.fixture def filter_id(w3): block_filter = w3.eth.filter("latest") return block_filter.filter_id diff --git a/tests/core/filtering/utils.py b/tests/core/filtering/utils.py index 3c470c0ba5..389de0061f 100644 --- a/tests/core/filtering/utils.py +++ b/tests/core/filtering/utils.py @@ -1,3 +1,7 @@ +from eth_tester import ( + EthereumTester, +) + from web3 import ( AsyncWeb3, Web3, @@ -11,10 +15,9 @@ ) -def _w3_fixture_logic(request): +def _w3_fixture_logic(request, backend_class): use_filter_middleware = request.param - provider = EthereumTesterProvider() - w3 = Web3(provider) + w3 = Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) w3.eth.default_account = w3.eth.accounts[0] if use_filter_middleware: w3.middleware_onion.add(LocalFilterMiddleware) @@ -43,10 +46,11 @@ def _emitter_fixture_logic( # --- async --- # -async def _async_w3_fixture_logic(request): +async def _async_w3_fixture_logic(request, backend_class): use_filter_middleware = request.param - provider = AsyncEthereumTesterProvider() - async_w3 = AsyncWeb3(provider) + async_w3 = AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) accounts = await async_w3.eth.accounts async_w3.eth.default_account = accounts[0] diff --git a/tests/core/middleware/test_name_to_address_middleware.py b/tests/core/middleware/test_name_to_address_middleware.py index e3c9196236..bb55c6949c 100644 --- a/tests/core/middleware/test_name_to_address_middleware.py +++ b/tests/core/middleware/test_name_to_address_middleware.py @@ -1,5 +1,8 @@ import pytest +from eth_tester import ( + EthereumTester, +) import pytest_asyncio from web3 import ( @@ -30,8 +33,11 @@ def address(self, name): @pytest.fixture -def _w3_setup(): - return Web3(provider=EthereumTesterProvider(), middleware=[]) +def _w3_setup(backend_class): + return Web3( + provider=EthereumTesterProvider(EthereumTester(backend=backend_class())), + middleware=[], + ) @pytest.fixture diff --git a/tests/core/middleware/test_transaction_signing.py b/tests/core/middleware/test_transaction_signing.py index ee1030b958..f2cb467074 100644 --- a/tests/core/middleware/test_transaction_signing.py +++ b/tests/core/middleware/test_transaction_signing.py @@ -10,11 +10,13 @@ LocalAccount, ) import eth_keys +from eth_tester import ( + EthereumTester, +) from eth_tester.exceptions import ( ValidationError, ) from eth_utils import ( - ValidationError as EthUtilsValidationError, is_hexstr, to_bytes, to_hex, @@ -226,7 +228,7 @@ def hex_to_bytes(s): ), ( {"gas": 21000, "gasPrice": 0, "value": 1}, - EthUtilsValidationError, + Exception, MIXED_KEY_MIXED_TYPE, ADDRESS_1, ), @@ -244,7 +246,7 @@ def hex_to_bytes(s): ( { "value": 22, - "maxFeePerGas": 20**9, + "maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, }, -1, @@ -310,9 +312,9 @@ def assert_method_and_txn_signed(actual, expected): assert is_hexstr(raw_txn) -@pytest.fixture() -def w3(): - _w3 = Web3(EthereumTesterProvider()) +@pytest.fixture +def w3(backend_class): + _w3 = Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) _w3.eth.default_account = _w3.eth.accounts[0] return _w3 @@ -494,8 +496,10 @@ async def async_w3_dummy(request_mocker): @pytest_asyncio.fixture -async def async_w3(): - _async_w3 = AsyncWeb3(AsyncEthereumTesterProvider()) +async def async_w3(backend_class): + _async_w3 = AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) accounts = await _async_w3.eth.accounts _async_w3.eth.default_account = accounts[0] return _async_w3 diff --git a/tests/core/module-class/test_module.py b/tests/core/module-class/test_module.py index 8fe97b7d7f..df641da306 100644 --- a/tests/core/module-class/test_module.py +++ b/tests/core/module-class/test_module.py @@ -37,8 +37,9 @@ def test_attach_methods_to_module(web3_with_external_modules): } ) - assert w3.eth.chain_id == 131277322940537 - assert w3.module1.property1 == 131277322940537 + configured_chain_id = w3.provider.ethereum_tester.backend.chain.chain_id + assert w3.eth.chain_id == configured_chain_id + assert w3.module1.property1 == configured_chain_id account = w3.eth.accounts[0] assert w3.eth.get_balance(account, "latest") == 1000000000000000000000000 diff --git a/tests/core/utilities/conftest.py b/tests/core/utilities/conftest.py index 8274f13e5c..263f1a722d 100644 --- a/tests/core/utilities/conftest.py +++ b/tests/core/utilities/conftest.py @@ -1,5 +1,9 @@ import pytest +from eth_tester import ( + EthereumTester, +) + from web3.main import ( Web3, ) @@ -9,6 +13,5 @@ @pytest.fixture(scope="module") -def w3(): - provider = EthereumTesterProvider() - return Web3(provider) +def w3(backend_class): + return Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) diff --git a/tests/core/utilities/test_abi.py b/tests/core/utilities/test_abi.py index 3bd2a046e3..1de50f19ce 100644 --- a/tests/core/utilities/test_abi.py +++ b/tests/core/utilities/test_abi.py @@ -168,7 +168,7 @@ ABI_ERROR = ABIError({"type": "error", "name": "error"}) -@pytest.fixture() +@pytest.fixture def contract_abi() -> ABI: return CONTRACT_ABI diff --git a/tests/core/utilities/test_async_transaction.py b/tests/core/utilities/test_async_transaction.py index 4ec077451d..779f6a8fc3 100644 --- a/tests/core/utilities/test_async_transaction.py +++ b/tests/core/utilities/test_async_transaction.py @@ -74,15 +74,14 @@ async def test_async_fill_transaction_defaults_for_all_params(async_w3): } -@pytest.mark.asyncio() -async def test_async_fill_transaction_defaults_nondynamic_tranaction_fee(async_w3): - gasPrice_transaction = { - "gasPrice": 10, +@pytest.mark.asyncio +async def test_async_fill_transaction_defaults_non_dynamic_transaction_fee(async_w3): + gas_price_transaction = { + "gasPrice": 10**9, } default_transaction = await async_fill_transaction_defaults( - async_w3, gasPrice_transaction + async_w3, gas_price_transaction ) - assert none_in_dict(DYNAMIC_FEE_TXN_PARAMS, default_transaction) diff --git a/tests/core/web3-module/test_web3_inheritance.py b/tests/core/web3-module/test_web3_inheritance.py index 8201a9ed8b..40d4c15fb2 100644 --- a/tests/core/web3-module/test_web3_inheritance.py +++ b/tests/core/web3-module/test_web3_inheritance.py @@ -9,4 +9,7 @@ class InheritsFromWeb3(Web3): pass inherited_w3 = InheritsFromWeb3(EthereumTesterProvider()) - assert inherited_w3.eth.chain_id == 131277322940537 + assert ( + inherited_w3.eth.chain_id + == inherited_w3.provider.ethereum_tester.backend.chain.chain_id + ) diff --git a/tests/ens/conftest.py b/tests/ens/conftest.py index c9c1115b18..9cd98a2b97 100644 --- a/tests/ens/conftest.py +++ b/tests/ens/conftest.py @@ -163,8 +163,8 @@ def ens(ens_setup, mocker): # session scope for performance @pytest.fixture(scope="session") -def ens_setup(): - w3 = Web3(EthereumTesterProvider(EthereumTester())) +def ens_setup(backend_class): + w3 = Web3(EthereumTesterProvider(EthereumTester(backend=backend_class()))) # ** Set up ENS contracts ** @@ -355,7 +355,7 @@ def ens_setup(): return ENS.from_web3(w3, ens_contract.address) -@pytest.fixture() +@pytest.fixture def TEST_ADDRESS(address_conversion_func): return address_conversion_func("0x000000000000000000000000000000000000dEaD") @@ -364,8 +364,10 @@ def TEST_ADDRESS(address_conversion_func): @pytest_asyncio.fixture(scope="session") -def async_w3(): - _async_w3 = AsyncWeb3(AsyncEthereumTesterProvider()) +def async_w3(backend_class): + _async_w3 = AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) return _async_w3 @@ -461,8 +463,11 @@ def event_loop(): # add session scope with above session-scoped `event_loop` for better performance @pytest_asyncio.fixture(scope="session") -async def async_ens_setup(async_w3): +async def async_ens_setup(backend_class): # ** Set up ENS contracts ** + async_w3 = AsyncWeb3( + AsyncEthereumTesterProvider(EthereumTester(backend=backend_class())) + ) # remove account that creates ENS, so test transactions don't have write access accounts = await async_w3.eth.accounts @@ -479,7 +484,7 @@ async def async_ens_setup(async_w3): ) reverse_tld_namehash = bytes32( 0xA097F6721CE401E757D1223A763FEF49B8B5F90BB18567DDB86FD205DFF71D34 - ) # noqa: E501 + ) reverser_namehash = bytes32( 0x91D1777781884D03A6757A803996E38DE2A42967FB37EEACA72729271025A9E2 ) diff --git a/tests/ens/test_get_text.py b/tests/ens/test_get_text.py index 6befbc36d0..547c6c0516 100644 --- a/tests/ens/test_get_text.py +++ b/tests/ens/test_get_text.py @@ -1,7 +1,7 @@ import pytest -from eth_utils import ( - ValidationError as EthUtilsValidationError, +from eth_tester.exceptions import ( + ValidationError as EthTesterValidationError, ) from ens.exceptions import ( @@ -31,9 +31,14 @@ def test_set_text_fails_with_bad_address(ens): address = ens.w3.eth.accounts[2] ens.setup_address("tester.eth", address) zero_address = "0x" + "00" * 20 - with pytest.raises(EthUtilsValidationError): + with pytest.raises(EthTesterValidationError): ens.set_text( - "tester.eth", "url", "http://example.com", transact={"from": zero_address} + "tester.eth", + "url", + "http://example.com", + # add gas so we don't call eth_estimateGas which can fail the transaction + # in a different way + transact={"from": zero_address, "gas": 222_222}, ) # teardown @@ -49,8 +54,8 @@ def test_set_text_pass_in_transaction_dict(ens): "avatar", "example.jpeg", transact={ - "maxFeePerGas": Web3.to_wei(100, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(100, "gwei"), + "maxFeePerGas": Web3.to_wei(1, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(1, "gwei"), }, ) assert ens.get_text("tester.eth", "url") == "http://example.com" @@ -102,9 +107,14 @@ async def test_async_set_text_fails_with_bad_address(async_ens): address = accounts[2] await async_ens.setup_address("tester.eth", address) zero_address = "0x" + "00" * 20 - with pytest.raises(EthUtilsValidationError): + with pytest.raises(EthTesterValidationError): await async_ens.set_text( - "tester.eth", "url", "http://example.com", transact={"from": zero_address} + "tester.eth", + "url", + "http://example.com", + # add gas so we don't call eth_estimateGas which can fail the transaction + # in a different way + transact={"from": zero_address, "gas": 222_222}, ) # teardown @@ -125,8 +135,8 @@ async def async_test_set_text_pass_in_transaction_dict(async_ens): "avatar", "example.jpeg", transact={ - "maxFeePerGas": Web3.to_wei(100, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(100, "gwei"), + "maxFeePerGas": Web3.to_wei(1, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(1, "gwei"), }, ) assert await async_ens.get_text("tester.eth", "url") == "http://example.com" diff --git a/tests/ens/test_setup_name.py b/tests/ens/test_setup_name.py index cef22d9bf2..752e39b7d4 100644 --- a/tests/ens/test_setup_name.py +++ b/tests/ens/test_setup_name.py @@ -92,7 +92,7 @@ def test_setup_name_default_address(ens): assert not ens.name(new_resolution) assert ens.owner(name) == owner assert ens.address(name) == new_resolution - ens.setup_name(name) + ens.setup_name(name, transact={"gas": 222_222}) assert ens.name(new_resolution) == name ens.setup_name(None, new_resolution) @@ -190,7 +190,7 @@ async def test_async_setup_name_default_address(async_ens): assert not await async_ens.name(new_resolution) assert await async_ens.owner(name) == owner assert await async_ens.address(name) == new_resolution - await async_ens.setup_name(name) + await async_ens.setup_name(name, transact={"gas": 222_222}) assert await async_ens.name(new_resolution) == name await async_ens.setup_name(None, new_resolution) diff --git a/tests/integration/ethereum_tester/__init__.py b/tests/integration/ethereum_tester/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/test_ethereum_tester.py b/tests/integration/ethereum_tester/common.py similarity index 73% rename from tests/integration/test_ethereum_tester.py rename to tests/integration/ethereum_tester/common.py index e7d9269df3..02132dbab4 100644 --- a/tests/integration/test_ethereum_tester.py +++ b/tests/integration/ethereum_tester/common.py @@ -1,36 +1,16 @@ import functools import pytest from typing import ( - cast, + TYPE_CHECKING, ) -from eth_tester import ( - EthereumTester, -) from eth_tester.exceptions import ( TransactionFailed, ) -from eth_typing import ( - ChecksumAddress, -) from eth_utils import ( - is_checksum_address, - is_dict, is_integer, ) -from web3 import ( - Web3, -) -from web3._utils.contract_sources.contract_data._custom_contract_data import ( - EMITTER_ENUM, -) -from web3._utils.contract_sources.contract_data.panic_errors_contract import ( - PANIC_ERRORS_CONTRACT_DATA, -) -from web3._utils.contract_sources.contract_data.storage_contract import ( - STORAGE_CONTRACT_DATA, -) from web3._utils.module_testing import ( EthModuleTest, NetModuleTest, @@ -43,187 +23,16 @@ MethodUnavailable, Web3TypeError, ) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) -from web3.types import ( # noqa: F401 +from web3.types import ( BlockData, ) -# set up the keyfile account with a known address (same from geth setup) -KEYFILE_ACCOUNT_PKEY = ( - "0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d" -) -KEYFILE_ACCOUNT_ADDRESS = "0xdC544d1AA88Ff8bbd2F2AeC754B1F1e99e1812fd" - - -def _deploy_contract(w3, contract_factory): - deploy_txn_hash = contract_factory.constructor().transact( - {"from": w3.eth.default_account} - ) - deploy_receipt = w3.eth.wait_for_transaction_receipt(deploy_txn_hash) - assert is_dict(deploy_receipt) - contract_address = deploy_receipt["contractAddress"] - assert is_checksum_address(contract_address) - return contract_factory(contract_address) - - -@pytest.fixture(scope="module") -def eth_tester(): - _eth_tester = EthereumTester() - return _eth_tester - - -@pytest.fixture(scope="module") -def eth_tester_provider(eth_tester): - provider = EthereumTesterProvider(eth_tester) - return provider - - -def _eth_tester_state_setup(w3): - provider = cast(EthereumTesterProvider, w3.provider) - provider.ethereum_tester.add_account(KEYFILE_ACCOUNT_PKEY) - - # fund the account - w3.eth.send_transaction( - { - "from": ChecksumAddress(w3.eth.default_account), - "to": KEYFILE_ACCOUNT_ADDRESS, - "value": w3.to_wei(0.5, "ether"), - "gas": 21000, - "gasPrice": 10**9, # needs to be > base_fee post London - } +if TYPE_CHECKING: + from web3 import ( + Web3, ) -@pytest.fixture(scope="module") -def w3(eth_tester_provider): - _w3 = Web3(eth_tester_provider) - _w3.eth.default_account = _w3.eth.accounts[0] - _eth_tester_state_setup(_w3) - return _w3 - - -@pytest.fixture(scope="module") -def math_contract_deploy_txn_hash(w3, math_contract_factory): - deploy_txn_hash = math_contract_factory.constructor().transact( - {"from": w3.eth.default_account} - ) - return deploy_txn_hash - - -@pytest.fixture(scope="module") -def math_contract(w3, math_contract_factory, math_contract_deploy_txn_hash): - deploy_receipt = w3.eth.wait_for_transaction_receipt(math_contract_deploy_txn_hash) - assert is_dict(deploy_receipt) - contract_address = deploy_receipt["contractAddress"] - assert is_checksum_address(contract_address) - return math_contract_factory(contract_address) - - -@pytest.fixture(scope="module") -def math_contract_address(math_contract, address_conversion_func): - return address_conversion_func(math_contract.address) - - -@pytest.fixture(scope="module") -def storage_contract(w3): - contract_factory = w3.eth.contract(**STORAGE_CONTRACT_DATA) - return _deploy_contract(w3, contract_factory) - - -@pytest.fixture(scope="module") -def emitter_contract(w3, emitter_contract_factory): - return _deploy_contract(w3, emitter_contract_factory) - - -@pytest.fixture(scope="module") -def emitter_contract_address(emitter_contract, address_conversion_func): - return address_conversion_func(emitter_contract.address) - - -@pytest.fixture(scope="module") -def empty_block(w3): - w3.testing.mine() - block = w3.eth.get_block("latest") - assert not block["transactions"] - return block - - -@pytest.fixture(scope="module") -def block_with_txn(w3): - txn_hash = w3.eth.send_transaction( - { - "from": ChecksumAddress(w3.eth.default_account), - "to": ChecksumAddress(w3.eth.default_account), - "value": w3.to_wei(1, "gwei"), - "gas": 21000, - "gasPrice": w3.to_wei( - 10**9, "gwei" - ), # needs to be > base_fee post London - } - ) - txn = w3.eth.get_transaction(txn_hash) - block = w3.eth.get_block(txn["blockNumber"]) - return block - - -@pytest.fixture(scope="module") -def mined_txn_hash(block_with_txn): - return block_with_txn["transactions"][0] - - -@pytest.fixture(scope="module") -def block_with_txn_with_log(w3, emitter_contract): - txn_hash = emitter_contract.functions.logDouble( - which=EMITTER_ENUM["LogDoubleWithIndex"], - arg0=12345, - arg1=54321, - ).transact({"from": w3.eth.default_account}) - txn = w3.eth.get_transaction(txn_hash) - block = w3.eth.get_block(txn["blockNumber"]) - return block - - -@pytest.fixture(scope="module") -def txn_hash_with_log(block_with_txn_with_log): - return block_with_txn_with_log["transactions"][0] - - -@pytest.fixture(scope="module") -def revert_contract(w3, revert_contract_factory): - return _deploy_contract(w3, revert_contract_factory) - - -# -# Offchain Lookup Contract Setup -# -@pytest.fixture(scope="module") -def offchain_lookup_contract(w3, offchain_lookup_contract_factory): - return _deploy_contract(w3, offchain_lookup_contract_factory) - - -@pytest.fixture(scope="module") -def panic_errors_contract(w3): - panic_errors_contract_factory = w3.eth.contract(**PANIC_ERRORS_CONTRACT_DATA) - return _deploy_contract(w3, panic_errors_contract_factory) - - -@pytest.fixture(scope="module") -def keyfile_account_pkey(): - yield KEYFILE_ACCOUNT_PKEY - - -@pytest.fixture(scope="module") -def keyfile_account_address(): - yield KEYFILE_ACCOUNT_ADDRESS - - -@pytest.fixture -def keyfile_account_address_dual_type(keyfile_account_address, address_conversion_func): - yield keyfile_account_address - - def not_implemented(method, exc_type=NotImplementedError): @functools.wraps(method) def inner(*args, **kwargs): @@ -242,13 +51,12 @@ def func_wrapper(self, eth_tester, *args, **kwargs): func(self, eth_tester, *args, **kwargs) finally: eth_tester.enable_auto_mine_transactions() - eth_tester.mine_block() eth_tester.revert_to_snapshot(snapshot) return func_wrapper -class TestEthereumTesterWeb3Module(Web3ModuleTest): +class EthereumTesterWeb3Module(Web3ModuleTest): def _check_web3_client_version(self, client_version): assert client_version.startswith("EthereumTester/") @@ -270,7 +78,7 @@ def _check_web3_client_version(self, client_version): ) -class TestEthereumTesterEthModule(EthModuleTest): +class EthereumTesterEthModule(EthModuleTest): test_eth_sign = not_implemented(EthModuleTest.test_eth_sign, MethodUnavailable) test_eth_sign_ens_names = not_implemented( EthModuleTest.test_eth_sign_ens_names, MethodUnavailable @@ -469,11 +277,6 @@ def test_eth_call_old_contract_state( w3, math_contract, keyfile_account_address ) - def test_eth_chain_id(self, w3): - chain_id = w3.eth.chain_id - assert is_integer(chain_id) - assert chain_id == 131277322940537 - @disable_auto_mine def test_eth_wait_for_transaction_receipt_unmined( self, eth_tester, w3, keyfile_account_address_dual_type @@ -674,5 +477,5 @@ def test_eth_get_balance_with_block_identifier(self, w3: "Web3") -> None: assert later_balance > genesis_balance -class TestEthereumTesterNetModule(NetModuleTest): +class EthereumTesterNetModule(NetModuleTest): pass diff --git a/tests/integration/ethereum_tester/conftest.py b/tests/integration/ethereum_tester/conftest.py new file mode 100644 index 0000000000..c0ba0ea943 --- /dev/null +++ b/tests/integration/ethereum_tester/conftest.py @@ -0,0 +1,154 @@ +import pytest + +from eth_typing import ( + ChecksumAddress, +) +from eth_utils import ( + is_checksum_address, + is_dict, +) + +from web3._utils.contract_sources.contract_data._custom_contract_data import ( + EMITTER_ENUM, +) +from web3._utils.contract_sources.contract_data.panic_errors_contract import ( + PANIC_ERRORS_CONTRACT_DATA, +) +from web3._utils.contract_sources.contract_data.storage_contract import ( + STORAGE_CONTRACT_DATA, +) + +# set up the keyfile account with a known address (same from geth setup) +KEYFILE_ACCOUNT_PKEY = ( + "0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d" +) +KEYFILE_ACCOUNT_ADDRESS = "0xdC544d1AA88Ff8bbd2F2AeC754B1F1e99e1812fd" + + +def _deploy_contract(w3, contract_factory): + deploy_txn_hash = contract_factory.constructor().transact( + {"from": w3.eth.default_account} + ) + deploy_receipt = w3.eth.wait_for_transaction_receipt(deploy_txn_hash) + assert is_dict(deploy_receipt) + contract_address = deploy_receipt["contractAddress"] + assert is_checksum_address(contract_address) + return contract_factory(contract_address) + + +@pytest.fixture(scope="module") +def keyfile_account_pkey(): + yield KEYFILE_ACCOUNT_PKEY + + +@pytest.fixture(scope="module") +def keyfile_account_address(): + yield KEYFILE_ACCOUNT_ADDRESS + + +@pytest.fixture(scope="module") +def math_contract_deploy_txn_hash(w3, math_contract_factory): + deploy_txn_hash = math_contract_factory.constructor().transact( + {"from": w3.eth.default_account} + ) + return deploy_txn_hash + + +@pytest.fixture(scope="module") +def math_contract(w3, math_contract_factory, math_contract_deploy_txn_hash): + deploy_receipt = w3.eth.wait_for_transaction_receipt(math_contract_deploy_txn_hash) + assert is_dict(deploy_receipt) + contract_address = deploy_receipt["contractAddress"] + assert is_checksum_address(contract_address) + return math_contract_factory(contract_address) + + +@pytest.fixture(scope="module") +def math_contract_address(math_contract, address_conversion_func): + return address_conversion_func(math_contract.address) + + +@pytest.fixture(scope="module") +def storage_contract(w3): + contract_factory = w3.eth.contract(**STORAGE_CONTRACT_DATA) + return _deploy_contract(w3, contract_factory) + + +@pytest.fixture(scope="module") +def emitter_contract(w3, emitter_contract_factory): + return _deploy_contract(w3, emitter_contract_factory) + + +@pytest.fixture(scope="module") +def emitter_contract_address(emitter_contract, address_conversion_func): + return address_conversion_func(emitter_contract.address) + + +@pytest.fixture(scope="module") +def empty_block(w3): + w3.testing.mine() + block = w3.eth.get_block("latest") + assert not block["transactions"] + return block + + +@pytest.fixture(scope="module") +def block_with_txn(w3): + txn_hash = w3.eth.send_transaction( + { + "from": ChecksumAddress(w3.eth.default_account), + "to": ChecksumAddress(w3.eth.default_account), + "value": w3.to_wei(1, "gwei"), + "gas": 21000, + "gasPrice": w3.to_wei(1, "gwei"), # needs to be > base_fee post London + } + ) + txn = w3.eth.get_transaction(txn_hash) + block = w3.eth.get_block(txn["blockNumber"]) + return block + + +@pytest.fixture(scope="module") +def mined_txn_hash(block_with_txn): + return block_with_txn["transactions"][0] + + +@pytest.fixture(scope="module") +def block_with_txn_with_log(w3, emitter_contract): + txn_hash = emitter_contract.functions.logDouble( + which=EMITTER_ENUM["LogDoubleWithIndex"], + arg0=12345, + arg1=54321, + ).transact({"from": w3.eth.default_account}) + txn = w3.eth.get_transaction(txn_hash) + block = w3.eth.get_block(txn["blockNumber"]) + return block + + +@pytest.fixture(scope="module") +def txn_hash_with_log(block_with_txn_with_log): + return block_with_txn_with_log["transactions"][0] + + +@pytest.fixture(scope="module") +def revert_contract(w3, revert_contract_factory): + return _deploy_contract(w3, revert_contract_factory) + + +# +# Offchain Lookup Contract Setup +# +@pytest.fixture(scope="module") +def offchain_lookup_contract(w3, offchain_lookup_contract_factory): + return _deploy_contract(w3, offchain_lookup_contract_factory) + + +@pytest.fixture(scope="module") +def panic_errors_contract(w3): + panic_errors_contract_factory = w3.eth.contract(**PANIC_ERRORS_CONTRACT_DATA) + return _deploy_contract(w3, panic_errors_contract_factory) + + +@pytest.fixture +def keyfile_account_address_dual_type(keyfile_account_address, address_conversion_func): + yield keyfile_account_address diff --git a/tests/integration/ethereum_tester/test_eels.py b/tests/integration/ethereum_tester/test_eels.py new file mode 100644 index 0000000000..1af4ac75f3 --- /dev/null +++ b/tests/integration/ethereum_tester/test_eels.py @@ -0,0 +1,88 @@ +import pytest +from typing import ( + cast, +) + +from eth_tester import ( + EELSBackend, + EthereumTester, +) +from eth_typing import ( + ChecksumAddress, +) + +from web3 import ( + Web3, +) +from web3.providers.eth_tester import ( + EthereumTesterProvider, +) +from web3.types import ( # noqa: F401 + BlockData, +) + +from .common import ( + EthereumTesterEthModule, + EthereumTesterNetModule, + EthereumTesterWeb3Module, +) + + +def _eth_tester_state_setup(w3, keyfile_account_address, keyfile_account_pkey): + provider = cast(EthereumTesterProvider, w3.provider) + provider.ethereum_tester.add_account(keyfile_account_pkey) + + # fund the account + w3.eth.send_transaction( + { + "from": ChecksumAddress(w3.eth.default_account), + "to": keyfile_account_address, + "value": w3.to_wei(0.5, "ether"), + "gas": 21000, + "gasPrice": 10**9, # needs to be > base_fee post London + } + ) + + +@pytest.fixture(scope="module") +def eth_tester(): + return EthereumTester(backend=EELSBackend()) + + +@pytest.fixture(scope="module") +def w3(eth_tester, keyfile_account_address, keyfile_account_pkey): + _w3 = Web3(EthereumTesterProvider(eth_tester)) + _w3.eth.default_account = _w3.eth.accounts[0] + _eth_tester_state_setup(_w3, keyfile_account_address, keyfile_account_pkey) + return _w3 + + +# -- test classes -- # + + +class TestEthereumTesterWeb3Module(EthereumTesterWeb3Module): + pass + + +class TestEthereumTesterEthModule(EthereumTesterEthModule): + def test_eth_chain_id(self, w3): + chain_id = w3.eth.chain_id + assert chain_id == 1 + + @pytest.mark.xfail(reason="EELS backed does not yet support eth_feeHistory") + def test_eth_fee_history(self, w3: "Web3") -> None: + super().test_eth_fee_history(w3) + + @pytest.mark.xfail(reason="EELS backed does not yet support eth_feeHistory") + def test_eth_fee_history_with_integer( + self, w3: "Web3", empty_block: BlockData + ) -> None: + super().test_eth_fee_history_with_integer(w3, empty_block) + + @pytest.mark.xfail(reason="EELS backed does not yet support eth_feeHistory") + def test_eth_fee_history_no_reward_percentiles(self, w3: "Web3") -> None: + super().test_eth_fee_history_no_reward_percentiles(w3) + + +class TestEthereumTesterNetModule(EthereumTesterNetModule): + pass diff --git a/tests/integration/ethereum_tester/test_pyevm.py b/tests/integration/ethereum_tester/test_pyevm.py new file mode 100644 index 0000000000..08a157322f --- /dev/null +++ b/tests/integration/ethereum_tester/test_pyevm.py @@ -0,0 +1,74 @@ +import pytest +from typing import ( + cast, +) + +from eth_tester import ( + EthereumTester, + PyEVMBackend, +) +from eth_typing import ( + ChecksumAddress, +) + +from web3 import ( + Web3, +) +from web3.providers.eth_tester import ( + EthereumTesterProvider, +) +from web3.types import ( # noqa: F401 + BlockData, +) + +from .common import ( + EthereumTesterEthModule, + EthereumTesterNetModule, + EthereumTesterWeb3Module, +) + + +def _eth_tester_state_setup(w3, keyfile_account_address, keyfile_account_pkey): + provider = cast(EthereumTesterProvider, w3.provider) + provider.ethereum_tester.add_account(keyfile_account_pkey) + + # fund the account + w3.eth.send_transaction( + { + "from": ChecksumAddress(w3.eth.default_account), + "to": keyfile_account_address, + "value": w3.to_wei(0.5, "ether"), + "gas": 21000, + "gasPrice": 10**9, # needs to be > base_fee post London + } + ) + + +@pytest.fixture(scope="module") +def eth_tester(): + return EthereumTester(backend=PyEVMBackend()) + + +@pytest.fixture(scope="module") +def w3(eth_tester, keyfile_account_address, keyfile_account_pkey): + _w3 = Web3(EthereumTesterProvider(eth_tester)) + _w3.eth.default_account = _w3.eth.accounts[0] + _eth_tester_state_setup(_w3, keyfile_account_address, keyfile_account_pkey) + return _w3 + + +# -- test classes -- # + + +class TestEthereumTesterWeb3Module(EthereumTesterWeb3Module): + pass + + +class TestEthereumTesterEthModule(EthereumTesterEthModule): + def test_eth_chain_id(self, w3): + chain_id = w3.eth.chain_id + assert chain_id == 131277322940537 + + +class TestEthereumTesterNetModule(EthereumTesterNetModule): + pass diff --git a/tests/integration/go_ethereum/test_goethereum_ws/conftest.py b/tests/integration/go_ethereum/test_goethereum_ws/conftest.py index e987ec4572..67c3d7d5cb 100644 --- a/tests/integration/go_ethereum/test_goethereum_ws/conftest.py +++ b/tests/integration/go_ethereum/test_goethereum_ws/conftest.py @@ -6,6 +6,18 @@ from tests.utils import ( get_open_port, ) +from web3 import ( + AsyncWeb3, + WebSocketProvider, +) + + +@pytest.fixture(scope="module") +def w3(): + """ + Defined for the sake of overriding the `w3` in the `AsyncWeb3ModuleTest` test cases. + """ + return AsyncWeb3(WebSocketProvider()) @pytest.fixture(scope="module") diff --git a/tox.ini b/tox.ini index e76a59c989..e8af27fe42 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,10 @@ [tox] envlist= - py{38,39,310,311,312}-{ens,core,lint,wheel} - py{38,39,310,311,312}-integration-{goethereum,ethtester} + py{py}{38,39,310,311,312}-{ens,core}-pyevm + py{py}{310,311,312}-{ens,core}-eels + py{38,39,310,311,312}-integration-{goethereum,ethtester-pyevm} + py{310,311,312}-integration-ethtester-eels + py{38,39,310,311,312}-{lint,wheel} docs benchmark windows-wheel @@ -20,9 +23,12 @@ allowlist_externals=make,pre-commit install_command=python -m pip install {opts} {packages} usedevelop=True commands= - core: pytest {posargs:tests/core -m "not asyncio"} - core_async: pytest {posargs:tests/core -m asyncio} - ens: pytest {posargs:tests/ens --ignore=tests/ens/normalization/test_normalize_name_ensip15.py} + core-pyevm: pytest {posargs:tests/core -m "not asyncio" --backend=pyevm} + core-pyevm_async: pytest {posargs:tests/core -m asyncio --backend=pyevm} + core-eels: pytest {posargs:tests/core -m "not asyncio" --backend=eels} + core-eels_async: pytest {posargs:tests/core -m asyncio --backend=eels} + ens-pyevm: pytest {posargs:tests/ens --ignore=tests/ens/normalization/test_normalize_name_ensip15.py --backend=pyevm} + ens-eels: pytest {posargs:tests/ens --ignore=tests/ens/normalization/test_normalize_name_ensip15.py --backend=eels} ensip15: pytest {posargs:tests/ens/normalization/test_normalize_name_ensip15.py -q} integration-goethereum-ipc: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ipc.py -k "not Async"} integration-goethereum-ipc_async: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ipc.py -k Async} @@ -30,7 +36,8 @@ commands= integration-goethereum-http_async: pytest {posargs:tests/integration/go_ethereum/test_goethereum_http.py -k Async} integration-goethereum-legacy_ws: pytest {posargs:tests/integration/go_ethereum/test_goethereum_legacy_ws.py} integration-goethereum-ws: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ws} - integration-ethtester: pytest {posargs:tests/integration/test_ethereum_tester.py} + integration-ethtester-pyevm: pytest {posargs:tests/integration/ethereum_tester/test_pyevm.py} + integration-ethtester-eels: pytest {posargs:tests/integration/ethereum_tester/test_eels.py} docs: make check-docs-ci deps = .[test] diff --git a/web3/_utils/module.py b/web3/_utils/module.py index 63fc151232..bf32e0cdfa 100644 --- a/web3/_utils/module.py +++ b/web3/_utils/module.py @@ -27,7 +27,10 @@ def _validate_init_params_and_return_if_found(module_class: Any) -> List[str]: init_params_raw = list(inspect.signature(module_class.__init__).parameters) module_init_params = [ - param for param in init_params_raw if param not in ["self", "args", "kwargs"] + param + for param in init_params_raw + # pypy uses `obj` and `keywords` instead of `self` and `kwargs`, respectively + if param not in ["self", "obj", "args", "kwargs", "keywords"] ] if len(module_init_params) > 1: diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/eth_module.py index 68193d1900..12dd8cf7e3 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/eth_module.py @@ -3230,7 +3230,7 @@ def test_eth_send_transaction_no_gas( "from": keyfile_account_address_dual_type, "to": keyfile_account_address_dual_type, "value": Wei(1), - "maxFeePerGas": Wei(250 * 10**9), + "maxFeePerGas": Wei(2 * 10**9), "maxPriorityFeePerGas": Wei(2 * 10**9), } txn_hash = w3.eth.send_transaction(txn_params) @@ -3725,7 +3725,7 @@ def test_eth_send_raw_transaction( ) -> None: keyfile_account = w3.eth.account.from_key(keyfile_account_pkey) txn = { - "chainId": 131277322940537, # the chainId set for the fixture + "chainId": w3.eth.chain_id, "from": keyfile_account.address, "to": keyfile_account.address, "value": Wei(0), diff --git a/web3/providers/eth_tester/defaults.py b/web3/providers/eth_tester/defaults.py index 77edbf73bb..3b878559c5 100644 --- a/web3/providers/eth_tester/defaults.py +++ b/web3/providers/eth_tester/defaults.py @@ -243,7 +243,7 @@ def create_new_account(eth_tester: "EthereumTester") -> HexAddress: "eth": { "protocolVersion": static_return(63), "syncing": static_return(False), - "chainId": static_return(131277322940537), # from fixture generation file + "chainId": call_eth_tester("chain_id"), "feeHistory": call_eth_tester("get_fee_history"), "maxPriorityFeePerGas": static_return(10**9), "gasPrice": static_return(10**9), # must be >= base fee post-London @@ -301,6 +301,10 @@ def create_new_account(eth_tester: "EthereumTester") -> HexAddress: "getTransactionByHash": null_if_transaction_not_found( call_eth_tester("get_transaction_by_hash") ), + "eth_getBlockTransactionCountByHash": not_implemented, + "eth_getBlockTransactionCountByNumber": not_implemented, + "eth_getRawTransactionByBlockNumberAndIndex": not_implemented, + "eth_getRawTransactionByBlockHashAndIndex": not_implemented, "getTransactionByBlockHashAndIndex": get_transaction_by_block_hash_and_index, "getTransactionByBlockNumberAndIndex": get_transaction_by_block_number_and_index, # noqa: E501 "getTransactionReceipt": null_if_transaction_not_found( diff --git a/web3/providers/eth_tester/main.py b/web3/providers/eth_tester/main.py index 48fdd260fb..9ede4e2849 100644 --- a/web3/providers/eth_tester/main.py +++ b/web3/providers/eth_tester/main.py @@ -66,20 +66,30 @@ class AsyncEthereumTesterProvider(AsyncBaseProvider): ethereum_tester_middleware, ) - def __init__(self) -> None: + def __init__( + self, + ethereum_tester: Optional["EthereumTester"] = None, + api_endpoints: Optional[ + Dict[str, Dict[str, Callable[..., RPCResponse]]] + ] = None, + ) -> None: super().__init__() + if not ethereum_tester: + from eth_tester import ( + EthereumTester, + ) - # do not import eth_tester until runtime, it is not a default dependency - from eth_tester import ( - EthereumTester, - ) + ethereum_tester = EthereumTester() - from web3.providers.eth_tester.defaults import ( - API_ENDPOINTS, - ) + if not api_endpoints: + from web3.providers.eth_tester.defaults import ( + API_ENDPOINTS, + ) + + api_endpoints = API_ENDPOINTS - self.ethereum_tester = EthereumTester() - self.api_endpoints = API_ENDPOINTS + self.ethereum_tester = ethereum_tester + self.api_endpoints = api_endpoints async def request_func( self, async_w3: "AsyncWeb3", middleware_onion: "MiddlewareOnion" diff --git a/web3/scripts/release/test_wheel_install.sh b/web3/scripts/release/test_wheel_install.sh index df2b63ae3e..8573f47d5a 100755 --- a/web3/scripts/release/test_wheel_install.sh +++ b/web3/scripts/release/test_wheel_install.sh @@ -2,6 +2,7 @@ set -e rm -rf build dist +python --version python -m build cd $(mktemp -d) python -m venv venv-test