From 0ad633000e013929fe423168205934f406fdac0b Mon Sep 17 00:00:00 2001 From: pacrob <5199899+pacrob@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:17:07 -0700 Subject: [PATCH] remove ethpm module, related tests, docs --- .circleci/config.yml | 84 -- .gitignore | 1 - .gitmodules | 3 - .isort.cfg | 4 +- Dockerfile | 1 - MANIFEST.in | 4 - Makefile | 2 +- docs/ethpm.rst | 915 ------------------ docs/examples.rst | 74 -- docs/overview.rst | 7 - docs/releases.rst | 2 +- docs/toc.rst | 2 - docs/web3.main.rst | 4 - docs/web3.pm.rst | 56 -- setup.py | 7 +- tests/core/pm-module/conftest.py | 140 --- tests/core/pm-module/test_ens_integration.py | 145 --- tests/core/pm-module/test_pm_init.py | 130 --- tests/core/pm-module/test_registry.py | 96 -- .../pm-module/test_registry_integration.py | 185 ---- .../tools/pytest_ethereum/assets/greeter.json | 1 - tests/core/tools/pytest_ethereum/conftest.py | 34 - .../tools/pytest_ethereum/test_deployer.py | 85 -- .../core/tools/pytest_ethereum/test_linker.py | 82 -- .../pytest_ethereum/test_linker_utils.py | 90 -- tests/ethpm/_utils/test_backend_utils.py | 88 -- tests/ethpm/_utils/test_cache_utils.py | 20 - tests/ethpm/_utils/test_chain_utils.py | 61 -- tests/ethpm/_utils/test_contract_utils.py | 93 -- tests/ethpm/_utils/test_ipfs_utils.py | 122 --- tests/ethpm/_utils/test_registry_utils.py | 86 -- tests/ethpm/backends/test_http_backends.py | 48 - tests/ethpm/backends/test_ipfs_backends.py | 138 --- tests/ethpm/backends/test_registry_backend.py | 57 -- tests/ethpm/conftest.py | 270 ------ .../ethpm/integration/test_escrow_manifest.py | 60 -- .../integration/test_ipfs_integration.py | 70 -- tests/ethpm/test_contract.py | 182 ---- tests/ethpm/test_dependencies.py | 69 -- tests/ethpm/test_deployments.py | 218 ----- tests/ethpm/test_get_build_dependencies.py | 52 - tests/ethpm/test_get_deployments.py | 49 - tests/ethpm/test_package.py | 123 --- tests/ethpm/test_package_init.py | 143 --- tests/ethpm/test_uri.py | 289 ------ tests/ethpm/tools/test_builder.py | 835 ---------------- tests/ethpm/tools/test_checker.py | 151 --- tests/ethpm/validation/test_manifest.py | 235 ----- .../test_manifest_assets_are_valid.py | 37 - tests/ethpm/validation/test_misc.py | 62 -- tox.ini | 8 +- web3/main.py | 26 - web3/pm.py | 602 ------------ web3/tools/__init__.py | 4 - web3/tools/pytest_ethereum/README.md | 5 - web3/tools/pytest_ethereum/__init__.py | 0 web3/tools/pytest_ethereum/_utils.py | 145 --- web3/tools/pytest_ethereum/deployer.py | 48 - web3/tools/pytest_ethereum/exceptions.py | 22 - web3/tools/pytest_ethereum/linker.py | 128 --- web3/tools/pytest_ethereum/plugins.py | 33 - 61 files changed, 8 insertions(+), 6725 deletions(-) delete mode 100644 .gitmodules delete mode 100644 docs/ethpm.rst delete mode 100644 docs/web3.pm.rst delete mode 100644 tests/core/pm-module/conftest.py delete mode 100644 tests/core/pm-module/test_ens_integration.py delete mode 100644 tests/core/pm-module/test_pm_init.py delete mode 100644 tests/core/pm-module/test_registry.py delete mode 100644 tests/core/pm-module/test_registry_integration.py delete mode 100644 tests/core/tools/pytest_ethereum/assets/greeter.json delete mode 100644 tests/core/tools/pytest_ethereum/conftest.py delete mode 100644 tests/core/tools/pytest_ethereum/test_deployer.py delete mode 100644 tests/core/tools/pytest_ethereum/test_linker.py delete mode 100644 tests/core/tools/pytest_ethereum/test_linker_utils.py delete mode 100644 tests/ethpm/_utils/test_backend_utils.py delete mode 100644 tests/ethpm/_utils/test_cache_utils.py delete mode 100644 tests/ethpm/_utils/test_chain_utils.py delete mode 100644 tests/ethpm/_utils/test_contract_utils.py delete mode 100644 tests/ethpm/_utils/test_ipfs_utils.py delete mode 100644 tests/ethpm/_utils/test_registry_utils.py delete mode 100644 tests/ethpm/backends/test_http_backends.py delete mode 100644 tests/ethpm/backends/test_ipfs_backends.py delete mode 100644 tests/ethpm/backends/test_registry_backend.py delete mode 100644 tests/ethpm/conftest.py delete mode 100644 tests/ethpm/integration/test_escrow_manifest.py delete mode 100644 tests/ethpm/integration/test_ipfs_integration.py delete mode 100644 tests/ethpm/test_contract.py delete mode 100644 tests/ethpm/test_dependencies.py delete mode 100644 tests/ethpm/test_deployments.py delete mode 100644 tests/ethpm/test_get_build_dependencies.py delete mode 100644 tests/ethpm/test_get_deployments.py delete mode 100644 tests/ethpm/test_package.py delete mode 100644 tests/ethpm/test_package_init.py delete mode 100644 tests/ethpm/test_uri.py delete mode 100644 tests/ethpm/tools/test_builder.py delete mode 100644 tests/ethpm/tools/test_checker.py delete mode 100644 tests/ethpm/validation/test_manifest.py delete mode 100644 tests/ethpm/validation/test_manifest_assets_are_valid.py delete mode 100644 tests/ethpm/validation/test_misc.py delete mode 100644 web3/pm.py delete mode 100644 web3/tools/__init__.py delete mode 100644 web3/tools/pytest_ethereum/README.md delete mode 100644 web3/tools/pytest_ethereum/__init__.py delete mode 100644 web3/tools/pytest_ethereum/_utils.py delete mode 100644 web3/tools/pytest_ethereum/deployer.py delete mode 100644 web3/tools/pytest_ethereum/exceptions.py delete mode 100644 web3/tools/pytest_ethereum/linker.py delete mode 100644 web3/tools/pytest_ethereum/plugins.py diff --git a/.circleci/config.yml b/.circleci/config.yml index ca81e7f77f..b95a863b18 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,9 +20,6 @@ common: &common - restore_cache: keys: - cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} - - run: - name: checkout ethpm-spec submodule - command: git submodule update --init --recursive - run: name: install dependencies command: | @@ -49,9 +46,6 @@ docs_steps: &docs_steps - restore_cache: keys: - cache-docs-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} - - run: - name: checkout ethpm-spec submodule - command: git submodule update --init --recursive - run: name: install dependencies command: | @@ -159,43 +153,6 @@ geth_custom_steps: &geth_custom_steps - ~/.ethash key: cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} -ethpm_steps: ðpm_steps - working_directory: ~/repo - resource_class: xlarge - steps: - - checkout - - restore_cache: - keys: - - ethpm-cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} - - run: - name: install ipfs - command: - wget https://dist.ipfs.io/go-ipfs/v0.7.0/go-ipfs_v0.7.0_linux-amd64.tar.gz && - tar xvfz go-ipfs_v0.7.0_linux-amd64.tar.gz && - sudo cp go-ipfs/ipfs /usr/local/bin && - ipfs init - - run: - name: start ipfs node in background - command: ipfs daemon - background: true - - run: - name: checkout ethpm-spec submodule - command: git submodule update --init --recursive - - run: - name: install dependencies - command: | - python -m pip install --upgrade pip - python -m pip install tox - - run: - name: run tox - command: python -m tox -r - - save_cache: - paths: - - .tox - - ~/.cache/pip - - ~/.local - key: ethpm-cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} - orbs: win: circleci/windows@5.0.0 @@ -209,9 +166,6 @@ windows_steps: &windows_steps - restore_cache: keys: - windows-cache-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }} - - run: - name: checkout ethpm-spec submodule - command: git submodule update --init --recursive - run: name: install dependencies command: | @@ -272,15 +226,6 @@ jobs: environment: TOXENV: py38-ensip15 - py38-ethpm: - <<: *ethpm_steps - docker: - - image: cimg/python:3.8 - environment: - TOXENV: py38-ethpm - # Please don't use this key for any shenanigans - WEB3_INFURA_PROJECT_ID: $WEB3_INFURA_PROJECT_ID - py38-integration-goethereum-ipc: <<: *geth_steps docker: @@ -419,14 +364,6 @@ jobs: environment: TOXENV: py39-ensip15 - py39-ethpm: - <<: *ethpm_steps - docker: - - image: cimg/python:3.9 - environment: - TOXENV: py39-ethpm - WEB3_INFURA_PROJECT_ID: $WEB3_INFURA_PROJECT_ID - py39-integration-goethereum-ipc: <<: *geth_steps docker: @@ -565,14 +502,6 @@ jobs: environment: TOXENV: py310-ensip15 - py310-ethpm: - <<: *ethpm_steps - docker: - - image: cimg/python:3.10 - environment: - TOXENV: py310-ethpm - WEB3_INFURA_PROJECT_ID: $WEB3_INFURA_PROJECT_ID - py310-integration-goethereum-ipc: <<: *geth_steps docker: @@ -716,15 +645,6 @@ jobs: environment: TOXENV: py311-ensip15 - py311-ethpm: - <<: *ethpm_steps - docker: - - image: cimg/python:3.11 - environment: - TOXENV: py311-ethpm - # Please don't use this key for any shenanigans - WEB3_INFURA_PROJECT_ID: $WEB3_INFURA_PROJECT_ID - py311-integration-goethereum-ipc: <<: *geth_steps docker: @@ -850,7 +770,6 @@ workflows: - py38-lint - py38-ens - py38-ensip15 - - py38-ethpm - py38-integration-goethereum-ipc - py38-integration-goethereum-ipc_async - py38-integration-goethereum-ipc_flaky @@ -868,7 +787,6 @@ workflows: - py39-lint - py39-ens - py39-ensip15 - - py39-ethpm - py39-integration-goethereum-ipc - py39-integration-goethereum-ipc_async - py39-integration-goethereum-ipc_flaky @@ -886,7 +804,6 @@ workflows: - py310-lint - py310-ens - py310-ensip15 - - py310-ethpm - py310-integration-goethereum-ipc - py310-integration-goethereum-ipc_async - py310-integration-goethereum-ipc_flaky @@ -904,7 +821,6 @@ workflows: - py311-lint - py311-ens - py311-ensip15 - - py311-ethpm - py311-integration-goethereum-ipc - py311-integration-goethereum-ipc_async - py311-integration-goethereum-ipc_flaky diff --git a/.gitignore b/.gitignore index 55496f8038..5df988ba01 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,6 @@ docs/web3.providers.persistent.rst docs/web3.rst docs/web3.scripts.release.rst docs/web3.scripts.rst -docs/web3.tools.pytest_ethereum.rst docs/web3.tools.rst # Blockchain diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 482fa10b27..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "ethpm/ethpm-spec"] - path = ethpm/ethpm-spec - url = https://github.com/ethpm/ethpm-spec.git diff --git a/.isort.cfg b/.isort.cfg index ba2c498bdc..1056dcbbf8 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -8,9 +8,9 @@ combine_as_imports=true force_sort_within_sections=true include_trailing_comma=true extra_standard_library=pytest -known_first_party=web3,ens,ethpm +known_first_party=web3,ens known_third_party=eth_tester line_length=88 use_parentheses=true # skip `__init__.py` files because sometimes order of initialization is important -skip=__init__.py,web3/main.py,web3/utils/windows.py,ethpm/ethpm-spec/,ethpm/_utils/protobuf/ipfs_file_pb2.py, +skip=__init__.py,web3/main.py,web3/utils/windows.py, diff --git a/Dockerfile b/Dockerfile index 8fe1aabb26..6e8c6960d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,6 @@ RUN apt-get update && apt-get install -y libssl-dev COPY web3 ./web3/ COPY tests ./tests/ COPY ens ./ens/ -COPY ethpm ./ethpm/ COPY setup.py . COPY README.md . diff --git a/MANIFEST.in b/MANIFEST.in index a2e04927d8..581194bf29 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,7 +5,3 @@ recursive-exclude * __pycache__ recursive-exclude * *.py[co] recursive-include ens/specs * - -recursive-include ethpm/assets * -recursive-include ethpm/ethpm-spec/examples * -recursive-include ethpm/ethpm-spec/spec * diff --git a/Makefile b/Makefile index 7f654907a0..7939a82f0a 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ benchmark: tox -e benchmark build-docs: - sphinx-apidoc -o docs/ . setup.py "*conftest*" "tests" "ethpm" "web3/tools/*" + sphinx-apidoc -o docs/ . setup.py "*conftest*" "tests" "web3/tools/*" $(MAKE) -C docs clean $(MAKE) -C docs html $(MAKE) -C docs doctest diff --git a/docs/ethpm.rst b/docs/ethpm.rst deleted file mode 100644 index fe29503396..0000000000 --- a/docs/ethpm.rst +++ /dev/null @@ -1,915 +0,0 @@ -.. _ethpm: - -ethPM -===== - -.. warning:: - The ``ethPM`` module is no longer being maintained and will be deprecated with ``web3.py`` version 7. - -Overview --------- - -This is a Python implementation of the `Ethereum Smart Contract -Packaging -Specification V3 `__, -driven by discussions in `ERC -190 `__, `ERC -1123 `__, `ERC -1319 `__. - -``Py-EthPM`` is being built as a low-level library to help developers leverage the ethPM spec. Including ... - -- Parse and validate packages. -- Construct and publish new packages. -- Provide access to contract factory classes. -- Provide access to all of a package's deployments. -- Validate package bytecode matches compilation output. -- Validate deployed bytecode matches compilation output. -- Access to package’s dependencies. -- Native integration with compilation metadata. - -Package -------- - -The ``Package`` object will function much like the ``Contract`` class -provided by ``web3``. Rather than instantiating the base class provided -by ``ethpm``, you will instead use a ``classmethod`` which generates a -new ``Package`` class for a given package. - -``Package`` objects *must* be instantiated with a valid ``web3`` object. - -.. doctest:: - - >>> from ethpm import Package, get_ethpm_spec_dir - >>> from web3 import Web3 - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> ethpm_spec_dir = get_ethpm_spec_dir() - >>> owned_manifest_path = ethpm_spec_dir / 'examples' / 'owned' / 'v3.json' - >>> OwnedPackage = Package.from_file(owned_manifest_path, w3) - >>> assert isinstance(OwnedPackage, Package) - -For a closer look at how to interact with EthPM packages using web3, check out the -:ref:`examples page `. - -Properties -~~~~~~~~~~ - -Each ``Package`` exposes the following properties. - -.. autoclass:: ethpm.Package - :members: name, version, manifest_version, uri, __repr__, contract_types, build_dependencies, deployments - -.. py:attribute:: Package.w3 - - The ``Web3`` instance currently set on this ``Package``. The deployments available on a package are automatically filtered to only contain those belonging to the currently set ``w3`` instance. - -.. py:attribute:: Package.manifest - - The manifest dict used to instantiate a ``Package``. - - -Methods -~~~~~~~ - -Each ``Package`` exposes the following methods. - -.. autoclass:: ethpm.Package - :noindex: - :members: from_file, from_uri, update_w3, get_contract_factory, get_contract_instance - - -Validation -~~~~~~~~~~ - -The ``Package`` class currently verifies the following things. - -- Manifests used to instantiate a ``Package`` object conform to the `EthPM V3 Manifest Specification `__ and are tightly packed, with keys sorted alphabetically, and no trailing newline. - - -LinkableContract ----------------- - -`Py-EthPM` uses a custom subclass of ``Web3.contract.Contract`` to manage contract factories and instances which might require bytecode linking. To create a deployable contract factory, both the contract type's ``abi`` and ``deploymentBytecode`` must be available in the Package's manifest. - -.. doctest:: - - >>> from eth_utils import is_address - >>> from web3 import Web3 - >>> from ethpm import Package, ASSETS_DIR - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> escrow_manifest_path = ASSETS_DIR / 'escrow' / 'with_bytecode_v3.json' - - >>> # Try to deploy from unlinked factory - >>> EscrowPackage = Package.from_file(escrow_manifest_path, w3) - >>> EscrowFactory = EscrowPackage.get_contract_factory("Escrow") - >>> assert EscrowFactory.needs_bytecode_linking - >>> escrow_instance = EscrowFactory.constructor(w3.eth.accounts[0]).transact() - Traceback (most recent call last): - ... - ethpm.exceptions.BytecodeLinkingError: Contract cannot be deployed until its bytecode is linked. - - >>> # Deploy SafeSendLib - >>> SafeSendFactory = EscrowPackage.get_contract_factory("SafeSendLib") - >>> safe_send_tx_hash = SafeSendFactory.constructor().transact() - >>> safe_send_tx_receipt = w3.eth.wait_for_transaction_receipt(safe_send_tx_hash) - - >>> # Link Escrow factory to deployed SafeSendLib instance - >>> LinkedEscrowFactory = EscrowFactory.link_bytecode({"SafeSendLib": safe_send_tx_receipt.contractAddress}) - >>> assert LinkedEscrowFactory.needs_bytecode_linking is False - >>> escrow_tx_hash = LinkedEscrowFactory.constructor(w3.eth.accounts[0]).transact() - >>> escrow_tx_receipt = w3.eth.wait_for_transaction_receipt(escrow_tx_hash) - >>> assert is_address(escrow_tx_receipt.contractAddress) - - -Properties -~~~~~~~~~~ - -.. py:attribute:: LinkableContract.unlinked_references - - A list of link reference data for the deployment bytecode, if present in the manifest data used to generate a ``LinkableContract`` factory. Deployment bytecode link reference data must be present in a manifest in order to generate a factory for a contract which requires bytecode linking. - -.. py:attribute:: LinkableContract.linked_references - - A list of link reference data for the runtime bytecode, if present in the manifest data used to generate a ``LinkableContract`` factory. If you want to use the `web3` `Deployer` tool for a contract, then runtime bytecode link reference data must be present in a manifest. - -.. py:attribute:: LinkableContract.needs_bytecode_linking - - A boolean attribute used to indicate whether a contract factory has unresolved link references, which must be resolved before a new contract instance can be deployed or instantiated at a given address. - - -Methods -~~~~~~~ - -.. py:classmethod:: LinkableContract.link_bytecode(attr_dict) - - This method returns a newly created contract factory with the applied link references defined in the ``attr_dict``. This method expects ``attr_dict`` to be of the type ``Dict[`contract_name`: `address`]`` for all link references that are unlinked. - -URI Schemes and Backends ------------------------- - -BaseURIBackend -~~~~~~~~~~~~~~ - -``Py-EthPM`` uses the ``BaseURIBackend`` as the parent class for all of its URI backends. To write your own backend, it must implement the following methods. - -.. py:method:: BaseURIBackend.can_resolve_uri(uri) - - Return a bool indicating whether or not this backend is capable of resolving the given URI to a manifest. - A content-addressed URI pointing to valid manifest is said to be capable of "resolving". - -.. py:method:: BaseURIBackend.can_translate_uri(uri) - - Return a bool indicating whether this backend class can translate the given URI to a corresponding content-addressed URI. - A registry URI is said to be capable of "translating" if it points to another content-addressed URI in its respective on-chain registry. - -.. py:method:: BaseURIBackend.fetch_uri_contents(uri) - - Fetch the contents stored at the provided uri, if an available backend is capable of resolving the URI. Validates that contents stored at uri match the content hash suffixing the uri. - - -IPFS -~~~~ - -``Py-EthPM`` has multiple backends available to fetch/pin files to IPFS. -The IPFS backends rely on the now-unmaintained ``ipfshttpclient`` library. Because of -this, they are opt-in and may be installed via the ``ipfs`` web3 install extra. - -.. code-block:: bash - - $ pip install "web3[ipfs]" - - -The desired backend can be set via the environment variable: ``ETHPM_IPFS_BACKEND_CLASS``. - -- ``InfuraIPFSBackend`` (default) - - `https://ipfs.infura.io` -- ``IPFSGatewayBackend`` (temporarily deprecated) - - `https://ipfs.io/ipfs/` -- ``LocalIPFSBacked`` - - Connect to a local IPFS API gateway running on port 5001. -- ``DummyIPFSBackend`` - - Won't pin/fetch files to an actual IPFS node, but mocks out this behavior. - -.. py:method:: BaseIPFSBackend.pin_assets(file_or_directory_path) - - Pin asset(s) found at the given path and returns the pinned asset data. - - -HTTPS -~~~~~ - -``Py-EthPM`` offers a backend to fetch files from Github, ``GithubOverHTTPSBackend``. - -A valid content-addressed Github URI *must* conform to the following scheme, as described in `ERC1319 `__, to be used with this backend. - -.. code:: python - - https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha - - -.. py:method:: create_content_addressed_github_uri(uri) - - This util function will return a content-addressed URI, as defined by Github's `blob `__ scheme. To generate a content-addressed URI for any manifest stored on github, this function requires accepts a Github API uri that follows the following scheme. - -:: - - https://api.github.com/repos/:owner/:repo/contents/:path/:to/manifest.json - -.. doctest:: - - >>> from ethpm.uri import create_content_addressed_github_uri - - >>> owned_github_api_uri = "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/1.0.0.json" - >>> content_addressed_uri = "https://api.github.com/repos/ethpm/ethpm-spec/git/blobs/8f9dc767d4c8b31fec4a08d9c0858d4f37b83180" - - >>> actual_blob_uri = create_content_addressed_github_uri(owned_github_api_uri) - >>> assert actual_blob_uri == content_addressed_uri - - -Registry URIs -~~~~~~~~~~~~~ - -The URI to lookup a package from a registry should follow the following -format. (subject to change as the Registry Contract Standard makes its -way through the EIP process) - -:: - - scheme://address:chain_id/package_name@version - -- URI must be a string type -- ``scheme``: (required) ``ethpm`` or ``erc1319`` -- ``address``: (required) Must be a valid ENS domain or a valid checksum address - pointing towards a registry contract. -- ``chain_id``: Chain ID of the chain on which the registry lives. Defaults to Mainnet. Supported chains include... - - - 1: Mainnet - - 5: Goerli - - 11155111: Sepolia - -- ``package-name``: Must conform to the package-name as specified in - the - `EthPM-Spec `__. -- ``version``: The URI escaped version string, *should* conform to the - `semver `__ version numbering specification. - -Examples... - -- ``ethpm://packages.zeppelinos.eth/owned@1.0.0`` - -- ``ethpm://0x582AC4D8929f58c217d4a52aDD361AE470a8a4cD:1/ethregistrar@1.0.0`` - -To specify a specific asset within a package, you can namespace the target asset. - -- ``ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/sources/token.sol`` - -- ``ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/contractTypes/DSToken/abi`` - -- ``ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/deployments/mainnet/dai`` - - -Builder -------- - -The manifest Builder is a tool designed to help construct custom manifests. The builder is still under active development, and can only handle simple use-cases for now. - -To create a simple manifest -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For all manifests, the following ingredients are *required*. - -.. code:: python - - build( - {}, - package_name(str), - version(str), - manifest_version(str), ..., - ) - # Or - build( - init_manifest(package_name: str, version: str, manifest_version: str="ethpm/3") - ..., - ) - - -The builder (i.e. ``build()``) expects a dict as the first argument. This dict can be empty, or populated if you want to extend an existing manifest. - -.. doctest:: - - >>> from ethpm.tools.builder import * - - >>> expected_manifest = { - ... "name": "owned", - ... "version": "1.0.0", - ... "manifest": "ethpm/3" - ... } - >>> base_manifest = {"name": "owned"} - >>> built_manifest = build( - ... {}, - ... package_name("owned"), - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... ) - >>> extended_manifest = build( - ... base_manifest, - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... ) - >>> assert built_manifest == expected_manifest - >>> assert extended_manifest == expected_manifest - -With ``init_manifest()``, which populates "manifest" with "ethpm/3" (the only supported EthPM specification version), unless provided with an alternative "version". - -.. doctest:: - - >>> build( - ... init_manifest("owned", "1.0.0"), - ... ) - {'name': 'owned', 'version': '1.0.0', 'manifest': 'ethpm/3'} - - - -To return a ``Package`` -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - as_package(w3: Web3), - ) - -By default, the manifest builder returns a dict representing the manifest. To return a ``Package`` instance (instantiated with the generated manifest) from the builder, add the ``as_package()`` builder function with a valid ``web3`` instance to the end of the builder. - -.. doctest:: - - >>> from ethpm import Package - >>> from web3 import Web3 - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> built_package = build( - ... {}, - ... package_name("owned"), - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... as_package(w3), - ... ) - >>> assert isinstance(built_package, Package) - - -To validate a manifest -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - validate(), - ) - -By default, the manifest builder does *not* perform any validation that the generated fields are correctly formatted. There are two ways to validate that the built manifest conforms to the EthPM V3 Specification. - - Return a Package, which automatically runs validation. - - Add the ``validate()`` function to the end of the manifest builder. - -.. doctest:: - - >>> valid_manifest = build( - ... {}, - ... package_name("owned"), - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... validate(), - ... ) - >>> assert valid_manifest == {"name": "owned", "manifest": "ethpm/3", "version": "1.0.0"} - >>> invalid_manifest = build( - ... {}, - ... package_name("_InvalidPkgName"), - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... validate(), - ... ) - Traceback (most recent call last): - ethpm.exceptions.EthPMValidationError: Manifest invalid for schema version 2. Reason: '_InvalidPkgName' does not match '^[a-z][-a-z0-9]{0,255}$' - - -To write a manifest to disk -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - write_to_disk( - manifest_root_dir: Optional[Path], - manifest_name: Optional[str], - prettify: Optional[bool], - ), - ) - - -Writes the active manifest to disk. Will not overwrite an existing manifest with the same name and root directory. - -Defaults -- Writes manifest to current working directory (as returned by ``os.getcwd()``) unless a ``Path`` is provided as manifest_root_dir. -- Writes manifest with a filename of ``.json`` unless desired manifest name (which must end in ".json") is provided as manifest_name. -- Writes the minified manifest version to disk unless prettify is set to True - -.. doctest:: - - >>> from pathlib import Path - >>> import tempfile - >>> p = Path(tempfile.mkdtemp("temp")) - >>> build( - ... {}, - ... package_name("owned"), - ... manifest_version("ethpm/3"), - ... version("1.0.0"), - ... write_to_disk(manifest_root_dir=p, manifest_name="manifest.json", prettify=True), - ... ) - {'name': 'owned', 'manifest': 'ethpm/3', 'version': '1.0.0'} - >>> with open(str(p / "manifest.json")) as f: - ... actual_manifest = f.read() - >>> print(actual_manifest) - { - "manifest": "ethpm/3", - "name": "owned", - "version": "1.0.0" - } - - -To pin a manifest to IPFS -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - pin_to_ipfs( - backend: BaseIPFSBackend, - prettify: Optional[bool], - ), - ) - -Pins the active manifest to disk. Must be the concluding function in a builder set since it returns the IPFS pin data rather than returning the manifest for further processing. - - -To add meta fields -~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - description(str), - license(str), - authors(*args: str), - keywords(*args: str), - links(*kwargs: str), - ..., - ) - -.. doctest:: - - >>> BASE_MANIFEST = {"name": "owned", "manifest": "ethpm/3", "version": "1.0.0"} - >>> expected_manifest = { - ... "name": "owned", - ... "manifest": "ethpm/3", - ... "version": "1.0.0", - ... "meta": { - ... "authors": ["Satoshi", "Nakamoto"], - ... "description": "An awesome package.", - ... "keywords": ["auth"], - ... "license": "MIT", - ... "links": { - ... "documentation": "www.readthedocs.com/...", - ... "repo": "www.github.com/...", - ... "website": "www.website.com", - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... authors("Satoshi", "Nakamoto"), - ... description("An awesome package."), - ... keywords("auth"), - ... license("MIT"), - ... links(documentation="www.readthedocs.com/...", repo="www.github.com/...", website="www.website.com"), - ... ) - >>> assert expected_manifest == built_manifest - - -Compiler Output -~~~~~~~~~~~~~~~ - -To build a more complex manifest for solidity contracts, it is required that you provide standard-json output from the solidity compiler. Or for a more convenient experience, use the `EthPM CLI `__. - -Here is an example of how to compile the contracts and generate the standard-json output. More information can be found in the `Solidity Compiler `__ docs. - -.. code:: sh - - solc --allow-paths --standard-json < standard-json-input.json > owned_compiler_output.json - -Sample standard-json-input.json - -.. code:: json - - { - "language": "Solidity", - "sources": { - "Contract.sol": { - "urls": [""] - } - }, - "settings": { - "outputSelection": { - "*": { - "*": ["abi", "evm.bytecode.object"] - } - } - } - } - - -The ``compiler_output`` as used in the following examples is the entire value of the ``contracts`` key of the solc output, which contains compilation data for all compiled contracts. - - -To add a source -~~~~~~~~~~~~~~~ - -.. code:: python - - # To inline a source - build( - ..., - inline_source( - contract_name: str, - compiler_output: Dict[str, Any], - package_root_dir: Optional[Path] - ), - ..., - ) - # To pin a source - build( - ..., - pin_source( - contract_name: str, - compiler_output: Dict[str, Any], - ipfs_backend: BaseIPFSBackend, - package_root_dir: Optional[Path] - ), - ..., - ) - -There are two ways to include a contract source in your manifest. - -Both strategies require that either . . . - - The current working directory is set to the package root directory - or - - The package root directory is provided as an argument (``package_root_dir``) - - -To inline the source code directly in the manifest, use ``inline_source()`` or ``source_inliner()`` (to inline multiple sources from the same compiler_output), which requires the contract name and compiler output as args. - -.. note:: - - ``output_v3.json`` below is expected to be the standard-json output generated by the solidity compiler as described `here `_. The output must contain the ``abi`` and ``bytecode`` objects from compilation. - -.. doctest:: - - >>> import json - >>> from ethpm import ASSETS_DIR, get_ethpm_spec_dir - >>> ethpm_spec_dir = get_ethpm_spec_dir() - >>> owned_dir = ethpm_spec_dir / "examples" / "owned" / "contracts" - >>> compiler_output = json.loads((ASSETS_DIR / "owned" / "output_v3.json").read_text())['contracts'] - >>> expected_manifest = { - ... "name": "owned", - ... "version": "1.0.0", - ... "manifest": "ethpm/3", - ... "sources": { - ... "./Owned.sol": { - ... "content": """// SPDX-License-Identifier: MIT\npragma solidity ^0.6.8;\n\ncontract Owned """ - ... """{\n address owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }""" - ... """\n\n constructor() public {\n owner = msg.sender;\n }\n}""", - ... "type": "solidity", - ... "installPath": "./Owned.sol" - ... } - ... } - ... } - >>> # With `inline_source()` - >>> built_manifest = build( - ... BASE_MANIFEST, - ... inline_source("Owned", compiler_output, package_root_dir=owned_dir), - ... ) - >>> assert expected_manifest == built_manifest - >>> # With `source_inliner()` for multiple sources from the same compiler output - >>> inliner = source_inliner(compiler_output, package_root_dir=owned_dir) - >>> built_manifest = build( - ... BASE_MANIFEST, - ... inliner("Owned"), - ... # inliner("other_source"), etc... - ... ) - >>> assert expected_manifest == built_manifest - - -To include the source as a content-addressed URI, ``Py-EthPM`` can pin your source via the Infura IPFS API. As well as the contract name and compiler output, this function requires that you provide the desired IPFS backend to pin the contract sources. - -.. code:: python - - >>> import json - >>> from ethpm import ASSETS_DIR, get_ethpm_spec_dir - >>> from ethpm.backends.ipfs import get_ipfs_backend - >>> ethpm_spec_dir = get_ethpm_spec_dir() - >>> owned_dir = ethpm_spec_dir / "examples" / "owned" / "contracts" - >>> compiler_output = json.loads((ASSETS_DIR / "owned" / "output_v3.json").read_text())['contracts'] - >>> ipfs_backend = get_ipfs_backend() - >>> expected_manifest = { - ... "name": "owned", - ... "version": "1.0.0", - ... "manifest": "ethpm/3", - ... "sources": { - ... "./Owned.sol": { - ... "installPath": "./Owned.sol", - ... "type": "solidity", - ... "urls": ["ipfs://QmU8QUSt56ZoBDJgjjXvAZEPro9LmK1m2gjVG5Q4s9x29W"] - ... } - ... } - ... } - >>> # With `pin_source()` - >>> built_manifest = build( - ... BASE_MANIFEST, - ... pin_source("Owned", compiler_output, ipfs_backend, package_root_dir=owned_dir), - ... ) - >>> assert expected_manifest == built_manifest - >>> # With `source_pinner()` for multiple sources from the same compiler output - >>> pinner = source_pinner(compiler_output, ipfs_backend, package_root_dir=owned_dir) - >>> built_manifest = build( - ... BASE_MANIFEST, - ... pinner("Owned"), - ... # pinner("other_source"), etc - ... ) - >>> assert expected_manifest == built_manifest - - - -To add a contract type -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - contract_type( - contract_name: str, - compiler_output: Dict[str, Any], - alias: Optional[str], - abi: Optional[bool], - compiler: Optional[bool], - contract_type: Optional[bool], - deployment_bytecode: Optional[bool], - devdoc: Optional[bool], - userdoc: Optional[bool], - source_id: Optional[bool], - runtime_bytecode: Optional[bool] - ), - ..., - ) - -The default behavior of the manifest builder's ``contract_type()`` function is to populate the manifest with all of the contract type data found in the ``compiler_output``. - -.. doctest:: - - >>> expected_manifest = { - ... 'name': 'owned', - ... 'manifest': 'ethpm/3', - ... 'version': '1.0.0', - ... 'compilers': [ - ... {'name': 'solc', 'version': '0.6.8+commit.0bbfe453', 'settings': {'optimize': True}, 'contractTypes': ['Owned']} - ... ], - ... 'contractTypes': { - ... 'Owned': { - ... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... 'deploymentBytecode': { - ... 'bytecode': '0x6080604052348015600f57600080fd5b50600080546001600160a01b03191633179055603f80602f6000396000f3fe6080604052600080fdfea26469706673582212208cbf6c3ccde7837026b3ec9660a0e95f1dbee0ce985f6879d7bc7e422519cc7564736f6c63430006080033' - ... }, - ... 'sourceId': 'Owned.sol', - ... 'devdoc': {'methods': {}}, - ... 'userdoc': {'methods': {}} - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output) - ... ) - >>> assert expected_manifest == built_manifest - - -To select only certain contract type data to be included in your manifest, provide the desired fields as ``True`` keyword arguments. The following fields can be specified for inclusion in the manifest . . . - - ``abi`` - - ``compiler`` - - ``deployment_bytecode`` - - ``runtime_bytecode`` - - ``devdoc`` - - ``userdoc`` - - ``source_id`` - -.. doctest:: - - >>> expected_manifest = { - ... 'name': 'owned', - ... 'manifest': 'ethpm/3', - ... 'version': '1.0.0', - ... 'contractTypes': { - ... 'Owned': { - ... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output, abi=True) - ... ) - >>> assert expected_manifest == built_manifest - -If you would like to alias your contract type, provide the desired alias as a kwarg. This will automatically include the original contract type in a ``contractType`` field. Unless specific contract type fields are provided as kwargs, ``contractType`` will still default to including all available contract type data found in the compiler output. - -.. doctest:: - - >>> expected_manifest = { - ... 'name': 'owned', - ... 'manifest': 'ethpm/3', - ... 'version': '1.0.0', - ... 'contractTypes': { - ... 'OwnedAlias': { - ... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... 'contractType': 'Owned' - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output, alias="OwnedAlias", abi=True) - ... ) - >>> assert expected_manifest == built_manifest - - -To add a deployment -~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - deployment( - block_uri, - contract_instance, - contract_type, - address, - transaction=None, - block=None, - deployment_bytecode=None, - runtime_bytecode=None, - compiler=None, - ), - ..., - ) - -There are two strategies for adding a deployment to your manifest. - -.. py:function:: deployment(block_uri, contract_instance, contract_type, address, transaction=None, block=None, deployment_bytecode=None, runtime_bytecode=None, compiler=None) - -This is the simplest builder function for adding a deployment to a manifest. All arguments require keywords. This builder function requires a valid ``block_uri``, it's up to the user to be sure that multiple chain URIs representing the same blockchain are not included in the "deployments" object keys. - -``runtime_bytecode``, ``deployment_bytecode`` and ``compiler`` must all be validly formatted dicts according to the `EthPM Spec `__. If your contract has link dependencies, be sure to include them in the bytecode objects. - - -.. doctest:: - - >>> expected_manifest = { - ... 'name': 'owned', - ... 'manifest': 'ethpm/3', - ... 'version': '1.0.0', - ... 'deployments': { - ... 'blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef': { - ... 'Owned': { - ... 'contractType': 'Owned', - ... 'address': '0x582AC4D8929f58c217d4a52aDD361AE470a8a4cD', - ... } - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... deployment( - ... block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - ... contract_instance='Owned', - ... contract_type='Owned', - ... address='0x582AC4D8929f58c217d4a52aDD361AE470a8a4cD', - ... ), - ... ) - >>> assert expected_manifest == built_manifest - -.. py:function:: deployment_type(contract_instance, contract_type, deployment_bytecode=None, runtime_bytecode=None, compiler=None) - -This builder function simplifies adding the same contract type deployment across multiple chains. It requires both a ``contract_instance`` and ``contract_type`` argument (in many cases these are the same, though ``contract_type`` *must* always match its correspondent in the manifest's "contract_types") and all arguments require keywords. - -``runtime_bytecode``, ``deployment_bytecode`` and ``compiler`` must all be validly formatted dicts according to the `EthPM Spec `__. If your contract has link dependencies, be sure to include them in the bytecode objects. - -.. code:: python - - owned_type = deployment_type(contract_instance="Owned", contract_type="Owned") - escrow_type = deployment_type( - contract_instance = "Escrow", - contract_type = "Escrow", - deployment_bytecode = { - "bytecode": "0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001" - }, - runtime_bytecode = { - "bytecode": "0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029" - }, - compiler = { - "name": "solc", - "version": "0.4.24+commit.e67f0147.Emscripten.clang", - "settings": { - "optimize": True - } - } - ) - manifest = build( - package_name("escrow"), - version("1.0.0"), - manifest_version("ethpm/3"), - owned_type( - block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=owned_testnet_address, - ), - owned_type( - block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=owned_mainnet_address, - transaction=owned_mainnet_transaction, - block=owned_mainnet_block, - ), - escrow_type( - block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=escrow_testnet_address, - ), - escrow_type( - block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=escrow_mainnet_address, - transaction=escrow_mainnet_transaction, - ), - ) - -To add a build dependency -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - build_dependency( - package_name, - uri, - ), - ..., - ) - -.. py:function:: build_dependency(package_name, uri) - -To add a build dependency to your manifest, just provide the package's name and a supported, content-addressed URI. - -.. doctest:: - - >>> expected_manifest = { - ... 'name': 'owned', - ... 'manifest': 'ethpm/3', - ... 'version': '1.0.0', - ... 'buildDependencies': { - ... 'owned': 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW', - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... build_dependency('owned', 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW'), - ... ) - >>> assert expected_manifest == built_manifest - - -Checker -------- - -The manifest Checker is a tool designed to help validate manifests according to the natural language spec (link). - -To validate a manifest -~~~~~~~~~~~~~~~~~~~~~~ - -.. doctest:: - - >>> from ethpm.tools.checker import check_manifest - - >>> basic_manifest = {"name": "example", "version": "1.0.0", "manifest": "ethpm/3"} - >>> check_manifest(basic_manifest) - {'meta': "Manifest missing a suggested 'meta' field.", 'sources': 'Manifest is missing a sources field, which defines a source tree that should comprise the full source tree necessary to recompile the contracts contained in this release.', 'contractTypes': "Manifest does not contain any 'contractTypes'. Packages should only include contract types that can be found in the source files for this package. Packages should not include contract types from dependencies. Packages should not include abstract contracts in the contract types section of a release.", 'compilers': 'Manifest is missing a suggested `compilers` field.'} diff --git a/docs/examples.rst b/docs/examples.rst index b197f1763f..b647401147 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -386,80 +386,6 @@ Output: 1 -.. _ethpm_example: - -Working with Contracts via ethPM --------------------------------- - -.. warning:: - The ``ethPM`` module is no longer being maintained and will be deprecated with ``web3.py`` version 7. - -`ethPM `__ packages contain configured contracts ready for use. Web3's ``ethpm`` module (``web3.pm``) -extends Web3's native ``Contract`` module, with a few modifications for how you instantiate ``Contract`` factories and instances. - -All you need is the package name, version and ethPM registry address for the package you wish to use. -An ethPM registry is an on-chain datastore for the release data associated with an ethPM package. You can find some sample registries to explore in the `ethPM registry `__. Remember, you should only use packages from registries whose maintainer you trust not to inject malicious code! - -In this example we will use the ``ethregistrar@3.0.0`` package sourced from the ``ens.snakecharmers.eth`` registry. - -``web3.pm`` uses the ``Package`` class to represent an ethPM package. This object houses all of the contract assets -within a package, and exposes them via an API. So, before we can interact with our package, we need to generate -it as a ``Package`` instance. - -.. code-block:: python3 - - # Note. To use the web3.pm module, you will need to instantiate your w3 instance - # with a web3 provider connected to the chain on which your registry lives. - from web3 import Web3, IPCProvider - w3 = Web3(IPCProvider(...)) - - # The ethPM module is still experimental and subject to change, - # so for now we need to enable it via a temporary flag. - w3.enable_unstable_package_management_api() - - # Then we need to set the registry address that we want to use. - # This should be an ENS address, but can also be a checksummed contract address. - w3.pm.set_registry("ens.snakecharmers.eth") - - # This generates a Package instance of the target ethPM package. - ens_package = w3.pm.get_package("ethregistrar", "3.0.0") - - -Now that we have a ``Package`` representation of our target ethPM package, we can generate contract factories -and instances from this ``Package``. However, it's important to note that some packages might be missing -the necessary contract assets needed to generate an instance or a factory. You can use the -`ethPM CLI `__ to figure out the available contract types and deployments -within an ethPM package. - -.. code-block:: python3 - - # To interact with a deployment located in an ethPM package. - # Note. This will only expose deployments located on the - # chain of the connected provider (in this example, mainnet) - mainnet_registrar = ens_package.deployments.get_instance("BaseRegistrarImplementation") - - # Now you can treat mainnet_registrar like any other Web3 Contract instance! - mainnet_registrar.caller.balanceOf("0x123...") - > 0 - - mainnet_registrar.functions.approve("0x123", 100000).transact() - > 0x123abc... # tx_hash - - # To create a contract factory from a contract type located in an ethPM package. - registrar_factory = ens_package.get_contract_factory("BaseRegistrarImplementation") - - # Now you can treat registrar_factory like any other Web3 Contract factory to deploy new instances! - # Note. This will deploy new instances to the chain of the connected provider (in this example, mainnet) - registrar_factory.constructor(...).transact() - > 0x456def... # tx_hash - - # To connect your Package to a new chain - simply pass it a new Web3 instance - # connected to your provider of choice. Now your factories will automatically - # deploy to this new chain, and the deployments available on a package will - # be automatically filtered to those located on the new chain. - goerli_registrar = ens_package.update_w3(goerli_w3_instance) - - Working with an ERC20 Token Contract ------------------------------------ diff --git a/docs/overview.rst b/docs/overview.rst index 953046d63e..e1ed45bd1e 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -322,13 +322,6 @@ Some basic network properties are available on the ``web3.net`` object: - :attr:`web3.net.version` -ethPM -~~~~~ - -ethPM allows you to package up your contracts for reuse or use contracts from -another trusted registry. See the full details :ref:`here `. - - ENS ~~~ diff --git a/docs/releases.rst b/docs/releases.rst index 2e97049a1d..e73415000a 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -3178,7 +3178,7 @@ Released June 21, 2018 - ``from web3.auto.gethdev import w3`` - ``from web3.auto.infura import w3`` (After setting the ``INFURA_API_KEY`` environment variable) - - Alpha support for a new package management tool based on ethpm-spec, see :doc:`web3.pm` + - Alpha support for a new package management tool based on ethpm-spec - Bugfixes - Can now receive large responses in :class:`~web3.providers.websocket.WebsocketProvider` by diff --git a/docs/toc.rst b/docs/toc.rst index 1d7b44c7b9..eaf80a6d82 100644 --- a/docs/toc.rst +++ b/docs/toc.rst @@ -22,7 +22,6 @@ Table of Contents abi_types middleware internals - ethpm ens_overview examples troubleshooting @@ -38,7 +37,6 @@ Table of Contents web3.main web3.eth web3.beacon - web3.pm web3.net web3.geth web3.tracing diff --git a/docs/web3.main.rst b/docs/web3.main.rst index f75d74508a..11141a3e08 100644 --- a/docs/web3.main.rst +++ b/docs/web3.main.rst @@ -363,10 +363,6 @@ Each ``Web3`` instance also exposes these namespaced API modules. See :doc:`./web3.eth` -.. py:attribute:: Web3.pm - - See :doc:`./web3.pm` - .. py:attribute:: Web3.geth See :doc:`./web3.geth` diff --git a/docs/web3.pm.rst b/docs/web3.pm.rst deleted file mode 100644 index c29f0652fb..0000000000 --- a/docs/web3.pm.rst +++ /dev/null @@ -1,56 +0,0 @@ -Package Manager API -=================== - - -The ``web3.pm`` object exposes methods to interact with Packages as defined by `ERC 1123 `_. - -- To learn more about the EthPM spec, visit the `spec `__ or the `documentation `__. - - -.. WARNING:: - - The ``web3.pm`` API is still under development and likely to change quickly. - - Now is a great time to get familiar with the API, and test out writing - code that uses some of the great upcoming features. - - By default, access to this module has been turned off in the stable version of web3.py: - - .. code-block:: python - - >>> from web3 import Web3, IPCProvider - >>> w3 = Web3(IPCProvider(...)) - >>> w3.pm - ... - AttributeError: The Package Management feature is disabled by default ... - - In order to access these features, you can turn it on with... - - .. code-block:: python - - >>> w3.enable_unstable_package_management_api() - >>> w3.pm - - - -Methods -------- -The following methods are available on the ``web3.pm`` namespace. - -.. autoclass:: web3.pm.PM - :members: - -.. autoclass:: web3.pm.ERC1319Registry - :members: __init__, _release, _get_package_name, _get_all_package_ids, _get_release_id, _get_all_release_ids, _get_release_data, _generate_release_id, _num_package_ids, _num_release_ids - - -Creating your own Registry class --------------------------------- -If you want to implement your own registry and use it with ``web3.pm``, you must create a subclass that inherits from ``ERC1319Registry``, and implements all the `ERC 1319 standard methods `_ prefixed with an underscore in ``ERC1319Registry``. Then, you have to manually set it as the ``registry`` attribute on ``web3.pm``. - -.. code-block:: python - - custom_registry = CustomRegistryClass(address, w3) - w3.pm.registry = custom_registry - -One reason a user might want to create their own Registry class is if they build a custom Package Registry smart contract that has features beyond those specified in `ERC 1319 `_. For example, the ability to delete a release or some micropayment feature. Rather than accessing those functions directly on the contract instance, they can create a custom ``ERC1319Registry`` subclass to easily call both the standard & custom methods. diff --git a/setup.py b/setup.py index 52acef9110..2517e940de 100644 --- a/setup.py +++ b/setup.py @@ -39,16 +39,12 @@ "when-changed>=0.3.0", "build>=0.9.0", ], - "ipfs": [ - "ipfshttpclient==0.8.0a2", - ], } extras_require["dev"] = ( extras_require["tester"] + extras_require["linter"] + extras_require["docs"] - + extras_require["ipfs"] + extras_require["dev"] ) @@ -84,8 +80,7 @@ ], python_requires=">=3.8", extras_require=extras_require, - py_modules=["web3", "ens", "ethpm"], - entry_points={"pytest11": ["pytest_ethereum = web3.tools.pytest_ethereum.plugins"]}, + py_modules=["web3", "ens"], license="MIT", zip_safe=False, keywords="ethereum", diff --git a/tests/core/pm-module/conftest.py b/tests/core/pm-module/conftest.py deleted file mode 100644 index b220db39c9..0000000000 --- a/tests/core/pm-module/conftest.py +++ /dev/null @@ -1,140 +0,0 @@ -import pytest - -from eth_tester import ( - EthereumTester, - PyEVMBackend, -) -from eth_utils import ( - to_bytes, -) - -from ethpm import ( - Package, -) -from ethpm.contract import ( - LinkableContract, -) -from ethpm.tools import ( - get_ethpm_local_manifest, -) -from web3 import ( - Web3, -) -from web3.pm import ( - SimpleRegistry, -) -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) - -SOL_PACKAGE_ID_1 = to_bytes( - hexstr="0x60c5112b61159e6b42d54d945078394e9d5fc9c6ff0f3df78977006f8bbc06d4" -) -SOL_PACKAGE_ID_2 = to_bytes( - hexstr="0xdbcfb0bd7115bf659350d77bb22bb889ca8294f61b0ca480f8a47bb8fc904cc9" -) -SOL_PACKAGE_ID_3 = to_bytes( - hexstr="0xf3e4002c48a7f8f3485d62988317849c175340b66517c3b2993f725643eba84b" -) -SOL_RELEASE_ID_1 = to_bytes( - hexstr="0x13414014c4f3c0ee41f1ede8e612e0377ae741f3abaa8d22e84e6b3759334fe9" -) -SOL_RELEASE_ID_2 = to_bytes( - hexstr="0x30cb63a88e721b461e294fa212af64f12e9500b3892e0e65fa70090ab63afb4d" -) -SOL_RELEASE_ID_3 = to_bytes( - hexstr="0x73f5fafa3d9bd5080d9b27c092cd65fdbf7c8f982df4d5d0de22eb2cd56f4fcb" -) -SOL_RELEASE_ID_4 = to_bytes( - hexstr="0x7fc4e4c04e1a4e5cba315f8fce216f8a77e1a1dd7c6539635555f95d1042667f" -) - - -def setup_w3(): - genesis_overrides = {"gas_limit": 5500000} - custom_genesis_params = PyEVMBackend._generate_genesis_params( - overrides=genesis_overrides - ) - pyevm_backend = PyEVMBackend(genesis_parameters=custom_genesis_params) - t = EthereumTester(backend=pyevm_backend) - w3 = Web3(Web3.EthereumTesterProvider(ethereum_tester=t)) - w3.eth.default_account = w3.eth.accounts[0] - w3.eth._default_contract_factory = LinkableContract - with pytest.warns( - UserWarning, match="The ``ethPM`` module is no longer being maintained" - ): - w3.enable_unstable_package_management_api() - return w3 - - -def sol_registry(w3): - manifest = get_ethpm_local_manifest("simple-registry", "v3.json") - registry_package = Package(manifest, w3) - registry_deployer = Deployer(registry_package) - deployed_registry_package = registry_deployer.deploy("PackageRegistry") - assert isinstance(registry_package, Package) - registry = deployed_registry_package.deployments.get_instance("PackageRegistry") - return SimpleRegistry(registry.address, w3) - - -def release_packages(registry): - registry._release( - "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - ) - registry._release( - "package", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW" - ) - registry._release( - "package", "1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX" - ) - registry._release( - "package", "1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ" - ) - registry._release( - "package", "1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK" - ) - registry._release( - "package", "1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH" - ) - registry._release( - "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - ) - registry._release( - "package2", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT" - ) - registry._release( - "package3", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGA" - ) - registry._release( - "package4", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGB" - ) - registry._release( - "package5", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGC" - ) - return registry - - -# Module-level variables used here for efficiency -# Tests are written against the sample packages released -# in `release_packages()` above, if more tests are needed, -# they should take into account the releases that exist on a "loaded registry". -W3 = setup_w3() -FRESH_SOL_REGISTRY = sol_registry(W3) -LOADED_SOL_REGISTRY = release_packages(sol_registry(W3)) -SOL_PKG_IDS = (SOL_PACKAGE_ID_1, SOL_PACKAGE_ID_2, SOL_PACKAGE_ID_3) -SOL_RLS_IDS = (SOL_RELEASE_ID_1, SOL_RELEASE_ID_2, SOL_RELEASE_ID_3, SOL_RELEASE_ID_4) - - -@pytest.fixture -def w3(): - return W3 - - -@pytest.fixture -def empty_sol_registry(): - return FRESH_SOL_REGISTRY - - -@pytest.fixture -def loaded_sol_registry(): - return LOADED_SOL_REGISTRY, SOL_PKG_IDS, SOL_RLS_IDS diff --git a/tests/core/pm-module/test_ens_integration.py b/tests/core/pm-module/test_ens_integration.py deleted file mode 100644 index 06031f6431..0000000000 --- a/tests/core/pm-module/test_ens_integration.py +++ /dev/null @@ -1,145 +0,0 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ens import ( - ENS, -) -from ethpm import ( - ASSETS_DIR, -) -from web3.exceptions import ( - InvalidAddress, -) -from web3.pm import ( - SimpleRegistry, -) - - -def bytes32(val): - if isinstance(val, int): - result = to_bytes(val) - else: - raise TypeError(f"{val!r} could not be converted to bytes") - return result.rjust(32, b"\0") - - -@pytest.fixture -def ens_setup(deployer): - # todo: move to module level once ethpm alpha stable - ENS_MANIFEST = ASSETS_DIR / "ens" / "v3.json" - ens_deployer = deployer(ENS_MANIFEST) - w3 = ens_deployer.package.w3 - - # ** Set up ENS contracts ** - - # remove account that creates ENS, so test transactions don't have write access - accounts = w3.eth.accounts - ens_key = accounts.pop() - - # create ENS contract - # values borrowed from: - # https://github.com/ethereum/web3.py/blob/main/tests/ens/conftest.py#L109 - eth_labelhash = w3.keccak(text="eth") - eth_namehash = bytes32( - 0x93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE - ) - resolver_namehash = bytes32( - 0xFDD5D5DE6DD63DB72BBC2D487944BA13BF775B50A80805FE6FCABA9B0FBA88F5 - ) - ens_package = ens_deployer.deploy("ENSRegistry", transaction={"from": ens_key}) - ens_contract = ens_package.deployments.get_instance("ENSRegistry") - - # create public resolver - public_resolver_package = ens_deployer.deploy( - "PublicResolver", ens_contract.address, transaction={"from": ens_key} - ) - public_resolver = public_resolver_package.deployments.get_instance("PublicResolver") - - # set 'resolver.eth' to resolve to public resolver - ens_contract.functions.setSubnodeOwner(b"\0" * 32, eth_labelhash, ens_key).transact( - {"from": ens_key} - ) - - ens_contract.functions.setSubnodeOwner( - eth_namehash, w3.keccak(text="resolver"), ens_key - ).transact({"from": ens_key}) - - ens_contract.functions.setResolver( - resolver_namehash, public_resolver.address - ).transact({"from": ens_key}) - - public_resolver.functions.setAddr( - resolver_namehash, public_resolver.address - ).transact({"from": ens_key}) - - # create .eth auction registrar - eth_registrar_package = ens_deployer.deploy( - "FIFSRegistrar", - ens_contract.address, - eth_namehash, - transaction={"from": ens_key}, - ) - eth_registrar = eth_registrar_package.deployments.get_instance("FIFSRegistrar") - - # set '.eth' to resolve to the registrar - ens_contract.functions.setResolver(eth_namehash, public_resolver.address).transact( - {"from": ens_key} - ) - - public_resolver.functions.setAddr(eth_namehash, eth_registrar.address).transact( - {"from": ens_key} - ) - - # set owner of tester.eth to an account controlled by tests - ens_contract.functions.setSubnodeOwner( - eth_namehash, - w3.keccak(text="tester"), - w3.eth.accounts[ - 2 - ], # note that this does not have to be the default, only in the list - ).transact({"from": ens_key}) - - # make the registrar the owner of the 'eth' name - ens_contract.functions.setSubnodeOwner( - b"\0" * 32, eth_labelhash, eth_registrar.address - ).transact({"from": ens_key}) - return ENS.from_web3(w3, ens_contract.address) - - -@pytest.fixture -def ens(ens_setup, mocker): - mocker.patch("web3.middleware.stalecheck._is_fresh", return_value=True) - ens_setup.w3.eth.default_account = ens_setup.w3.eth.coinbase - with pytest.warns( - UserWarning, match="The ``ethPM`` module is no longer being maintained" - ): - ens_setup.w3.enable_unstable_package_management_api() - return ens_setup - - -def test_ens_must_be_set_before_ens_methods_can_be_used(ens): - w3 = ens.w3 - with pytest.raises(InvalidAddress): - w3.pm.set_registry("tester.eth") - - -@pytest.mark.xfail(reason="Need to properly add authorization as of 8/10/2022") -def test_web3_ens(ens): - w3 = ens.w3 - ns = ENS.from_web3(w3, ens.ens.address) - w3.ens = ns - registry = SimpleRegistry.deploy_new_instance(w3) - w3.ens.setup_address("tester.eth", registry.address) - actual_addr = ens.address("tester.eth") - w3.pm.set_registry("tester.eth") - assert w3.pm.registry.address == actual_addr - w3.pm.release_package( - "owned", "1.0.0", "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - ) - pkg_name, version, manifest_uri = w3.pm.get_release_data("owned", "1.0.0") - assert pkg_name == "owned" - assert version == "1.0.0" - assert manifest_uri == "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" diff --git a/tests/core/pm-module/test_pm_init.py b/tests/core/pm-module/test_pm_init.py deleted file mode 100644 index 8bf1920ebb..0000000000 --- a/tests/core/pm-module/test_pm_init.py +++ /dev/null @@ -1,130 +0,0 @@ -import json -import pytest - -from ethpm import ( - Package, -) -from ethpm.exceptions import ( - EthPMException, - InsufficientAssetsError, -) -from ethpm.tools import ( - get_ethpm_local_manifest, - get_ethpm_spec_manifest, -) - - -def test_pm_init_with_minimal_manifest(w3): - owned_manifest = get_ethpm_spec_manifest("owned", "v3.json") - pm = w3.pm.get_package_from_manifest(owned_manifest) - assert pm.name == "owned" - - -def test_get_contract_factory_raises_insufficient_assets_error(w3): - insufficient_owned_manifest = get_ethpm_spec_manifest("owned", "v3.json") - owned_package = w3.pm.get_package_from_manifest(insufficient_owned_manifest) - with pytest.raises(InsufficientAssetsError): - owned_package.get_contract_factory("Owned") - - -def test_get_contract_factory_with_valid_owned_manifest(w3): - owned_manifest = get_ethpm_local_manifest("owned", "with_contract_type_v3.json") - owned_package = w3.pm.get_package_from_manifest(owned_manifest) - owned_factory = owned_package.get_contract_factory("Owned") - tx_hash = owned_factory.constructor().transact() - tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - owned_address = tx_receipt.contractAddress - owned_instance = owned_package.get_contract_instance("Owned", owned_address) - assert owned_instance.abi == owned_factory.abi - - -def test_get_contract_factory_with_valid_safe_math_lib_manifest(w3): - safe_math_lib_manifest = get_ethpm_spec_manifest("safe-math-lib", "v3.json") - safe_math_package = w3.pm.get_package_from_manifest(safe_math_lib_manifest) - safe_math_factory = safe_math_package.get_contract_factory("SafeMathLib") - tx_hash = safe_math_factory.constructor().transact() - tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - safe_math_address = tx_receipt.contractAddress - safe_math_instance = safe_math_package.get_contract_instance( - "SafeMathLib", safe_math_address - ) - assert safe_math_instance.functions.safeAdd(1, 2).call() == 3 - - -def test_get_contract_factory_with_valid_escrow_manifest(w3): - escrow_manifest = get_ethpm_spec_manifest("escrow", "v3.json") - escrow_package = w3.pm.get_package_from_manifest(escrow_manifest) - escrow_factory = escrow_package.get_contract_factory("Escrow") - assert escrow_factory.needs_bytecode_linking - safe_send_factory = escrow_package.get_contract_factory("SafeSendLib") - safe_send_tx_hash = safe_send_factory.constructor().transact() - safe_send_tx_receipt = w3.eth.wait_for_transaction_receipt(safe_send_tx_hash) - safe_send_address = safe_send_tx_receipt.contractAddress - linked_escrow_factory = escrow_factory.link_bytecode( - {"SafeSendLib": safe_send_address} - ) - assert linked_escrow_factory.needs_bytecode_linking is False - escrow_tx_hash = linked_escrow_factory.constructor(w3.eth.accounts[0]).transact() - escrow_tx_receipt = w3.eth.wait_for_transaction_receipt(escrow_tx_hash) - escrow_address = escrow_tx_receipt.contractAddress - escrow_instance = linked_escrow_factory(address=escrow_address) - assert escrow_instance.functions.sender().call() == w3.eth.accounts[0] - - -def test_deploy_a_standalone_package_integration(w3): - standard_token_manifest = get_ethpm_local_manifest( - "standard-token", "with_bytecode_v3.json" - ) - token_package = w3.pm.get_package_from_manifest(standard_token_manifest) - # Added deployment bytecode to manifest to be able to generate factory - ERC20 = token_package.get_contract_factory("StandardToken") - # totalSupply = 100 - tx_hash = ERC20.constructor(100).transact() - tx_receipt = w3.eth.get_transaction_receipt(tx_hash) - address = tx_receipt["contractAddress"] - erc20 = w3.eth.contract(address=address, abi=ERC20.abi) - total_supply = erc20.functions.totalSupply().call() - assert total_supply == 100 - - -def test_pm_init_with_manifest_uri(w3, monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - dummy_standard_token_uri = "ipfs://QmQNffBrmbB3TuBCtYfYsJWJVLssatWXa3H6CkGeyNUySA" - pkg = w3.pm.get_package_from_uri(dummy_standard_token_uri) - assert isinstance(pkg, Package) - assert pkg.name == "standard-token" - - -@pytest.fixture -def tmp_ethpmdir(tmp_path): - owned_manifest = get_ethpm_spec_manifest("owned", "v3.json") - ethpmdir = tmp_path / "_ethpm_packages" - ethpmdir.mkdir() - owned_dir = ethpmdir / "owned" - owned_dir.mkdir() - manifest = owned_dir / "manifest.json" - manifest.touch() - manifest.write_text( - json.dumps(owned_manifest, sort_keys=True, separators=(",", ":")) - ) - return ethpmdir - - -def test_get_local_package(w3, tmp_ethpmdir): - pkg = w3.pm.get_local_package("owned", tmp_ethpmdir) - assert isinstance(pkg, Package) - assert pkg.name == "owned" - - -def test_get_local_package_with_invalid_ethpmdir(w3, tmp_path): - invalid_ethpmdir = tmp_path / "invalid" - invalid_ethpmdir.mkdir() - with pytest.raises(EthPMException, match="not a valid ethPM packages directory."): - w3.pm.get_local_package("owned", invalid_ethpmdir) - - -def test_get_local_package_with_uninstalled_package(w3, tmp_ethpmdir): - with pytest.raises(EthPMException, match="Package: safe-math not found in "): - w3.pm.get_local_package("safe-math", tmp_ethpmdir) diff --git a/tests/core/pm-module/test_registry.py b/tests/core/pm-module/test_registry.py deleted file mode 100644 index a4292dbbd3..0000000000 --- a/tests/core/pm-module/test_registry.py +++ /dev/null @@ -1,96 +0,0 @@ -from eth_utils import ( - is_address, -) - -from web3.pm import ( - ERC1319Registry, - SimpleRegistry, -) - - -def test_simple_registry_deploy_new_instance(w3): - registry = SimpleRegistry.deploy_new_instance(w3) - assert isinstance(registry, SimpleRegistry) - assert isinstance(registry, ERC1319Registry) - assert is_address(registry.address) - - -def test_registry_releases_properly(empty_sol_registry): - release_id_1 = empty_sol_registry._release( - "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - ) - release_id_2 = empty_sol_registry._release( - "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - ) - release_data_1 = empty_sol_registry._get_release_data(release_id_1) - release_data_2 = empty_sol_registry._get_release_data(release_id_2) - assert release_data_1[0] == "package" - assert release_data_1[1] == "1.0.0" - assert release_data_1[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - assert release_data_2[0] == "package1" - assert release_data_2[1] == "1.0.1" - assert release_data_2[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - - -def test_registry_get_all_package_ids_and_get_package_name(loaded_sol_registry): - registry, expected_ids, _ = loaded_sol_registry - package_ids = registry._get_all_package_ids() - assert len(package_ids) == 6 - assert package_ids[0] == expected_ids[0] - assert package_ids[1] == expected_ids[1] - assert package_ids[2] == expected_ids[2] - assert registry._get_package_name(package_ids[0]) == "package" - assert registry._get_package_name(package_ids[1]) == "package1" - assert registry._get_package_name(package_ids[2]) == "package2" - - -def test_registry_get_release_id_and_get_all_release_ids(loaded_sol_registry): - registry, _, expected_ids = loaded_sol_registry - release_ids = registry._get_all_release_ids("package") - assert len(release_ids) == 6 - assert release_ids[:3] == expected_ids[:3] - assert registry._get_release_id("package", "1.0.0") == expected_ids[0] - assert registry._get_release_id("package", "1.0.1") == expected_ids[1] - assert registry._get_release_id("package", "1.0.2") == expected_ids[2] - - -def test_registry_num_package_ids(loaded_sol_registry): - registry, _, _ = loaded_sol_registry - assert registry._num_package_ids() == 6 - - -def test_registry_num_release_ids(loaded_sol_registry): - registry, _, _ = loaded_sol_registry - assert registry._num_release_ids("package") == 6 - assert registry._num_release_ids("package1") == 1 - assert registry._num_release_ids("package2") == 1 - - -def test_registry_generate_release_id(loaded_sol_registry): - registry, _, expected_ids = loaded_sol_registry - assert registry._generate_release_id("package", "1.0.0") == expected_ids[0] - assert registry._generate_release_id("package", "1.0.1") == expected_ids[1] - assert registry._generate_release_id("package", "1.0.2") == expected_ids[2] - assert registry._generate_release_id("does-not-exist", "1.0.0") == expected_ids[3] - - -def test_registry_get_release_data(loaded_sol_registry): - registry, _, release_ids = loaded_sol_registry - release_data_1 = registry._get_release_data(release_ids[0]) - release_data_2 = registry._get_release_data(release_ids[1]) - release_data_3 = registry._get_release_data(release_ids[2]) - assert release_data_1 == ( - "package", - "1.0.0", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV", - ) - assert release_data_2 == ( - "package", - "1.0.1", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW", - ) - assert release_data_3 == ( - "package", - "1.0.2", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX", - ) diff --git a/tests/core/pm-module/test_registry_integration.py b/tests/core/pm-module/test_registry_integration.py deleted file mode 100644 index 08005c221a..0000000000 --- a/tests/core/pm-module/test_registry_integration.py +++ /dev/null @@ -1,185 +0,0 @@ -import pytest - -from eth_utils import ( - is_address, - to_checksum_address, -) - -from ethpm import ( - Package, -) -from ethpm.contract import ( - LinkableContract, -) -from ethpm.exceptions import ( - EthPMException, -) -from web3 import ( - Web3, -) -from web3.pm import ( - SimpleRegistry, - get_simple_registry_manifest, -) - - -@pytest.fixture -def fresh_w3(): - w3 = Web3(Web3.EthereumTesterProvider()) - w3.eth.default_account = w3.eth.accounts[0] - w3.eth._default_contract_factory = LinkableContract - with pytest.warns( - UserWarning, match="The ``ethPM`` module is no longer being maintained" - ): - w3.enable_unstable_package_management_api() - return w3 - - -def test_pm_get_package_from_manifest(w3): - manifest = get_simple_registry_manifest() - package = w3.pm.get_package_from_manifest(manifest) - assert isinstance(package, Package) - assert package.name == "simple-registry" - - -def test_pm_deploy_and_set_registry(fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - registry_address = fresh_w3.pm.deploy_and_set_registry() - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(registry_address) - - -def test_pm_set_registry(empty_sol_registry, fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - fresh_w3.pm.set_registry(address=to_checksum_address(empty_sol_registry.address)) - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(fresh_w3.pm.registry.address) - - -def test_pm_set_custom_registry(empty_sol_registry, fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - fresh_w3.pm.registry = empty_sol_registry - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(fresh_w3.pm.registry.address) - - -@pytest.mark.xfail(reason="Need to properly add authorization as of 8/10/2022") -def test_pm_must_set_registry_before_all_registry_interaction_functions(fresh_w3): - with pytest.raises(EthPMException): - fresh_w3.pm.release_package( - "package", "1.0.0", "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - ) - with pytest.raises(EthPMException): - fresh_w3.pm.get_release_id_data(b"invalid_release_id") - with pytest.raises(EthPMException): - fresh_w3.pm.get_release_id("package", "1.0.0") - with pytest.raises(EthPMException): - fresh_w3.pm.get_release_data("package", "1.0.0") - with pytest.raises(EthPMException): - fresh_w3.pm.get_package("package", "1.0.0") - with pytest.raises(EthPMException): - fresh_w3.pm.get_all_package_names() - with pytest.raises(EthPMException): - fresh_w3.pm.get_all_package_releases("package") - with pytest.raises(EthPMException): - fresh_w3.pm.get_release_count("package") - with pytest.raises(EthPMException): - fresh_w3.pm.get_package_count() - - -@pytest.mark.xfail(reason="Need to properly add authorization as of 8/10/2022") -def test_pm_release_package(empty_sol_registry, w3): - w3.pm.registry = empty_sol_registry - w3.pm.release_package( - "escrow", "1.0.0", "ipfs://QmTpYHEog4yfmgx5GgvNCRQyDeQyBD4FWxTkiUP64AH1QC" - ) - w3.pm.release_package( - "owned", "1.0.0", "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - ) - release_id_1 = w3.pm.get_release_id("escrow", "1.0.0") - release_id_2 = w3.pm.get_release_id("owned", "1.0.0") - package_data_1 = w3.pm.get_release_id_data(release_id_1) - package_data_2 = w3.pm.get_release_id_data(release_id_2) - assert package_data_1[0] == "escrow" - assert package_data_1[1] == "1.0.0" - assert package_data_1[2] == "ipfs://QmTpYHEog4yfmgx5GgvNCRQyDeQyBD4FWxTkiUP64AH1QC" - assert package_data_2[0] == "owned" - assert package_data_2[1] == "1.0.0" - assert package_data_2[2] == "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - - -def test_pm_get_release_data(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - package_data = w3.pm.get_release_data("package", "1.0.0") - assert package_data[0] == "package" - assert package_data[1] == "1.0.0" - assert package_data[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - - -def test_pm_get_all_package_names(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - all_pkgs = w3.pm.get_all_package_names() - assert all_pkgs == ( - "package", - "package1", - "package2", - "package3", - "package4", - "package5", - ) - - -def test_pm_package_count(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - assert w3.pm.get_package_count() == 6 - - -def test_pm_get_release_count(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - pkg_0_release_count = w3.pm.get_release_count("package") - pkg_1_release_count = w3.pm.get_release_count("package1") - pkg_2_release_count = w3.pm.get_release_count("package2") - assert pkg_0_release_count == 6 - assert pkg_1_release_count == 1 - assert pkg_2_release_count == 1 - - -def test_pm_get_all_package_versions(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - all_rls_pkg_0 = w3.pm.get_all_package_releases("package") - all_rls_pkg_1 = w3.pm.get_all_package_releases("package1") - all_rls_pkg_2 = w3.pm.get_all_package_releases("package2") - assert all_rls_pkg_0 == ( - ("1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV"), - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW"), - ("1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX"), - ("1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ"), - ("1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK"), - ("1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH"), - ) - assert all_rls_pkg_1 == ( - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ"), - ) - assert all_rls_pkg_2 == ( - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT"), - ) - - -def test_pm_get_package(loaded_sol_registry, w3, monkeypatch): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - w3.pm.deploy_and_set_registry() - w3.pm.release_package( - "owned", "1.0.0", "ipfs://QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - ) - pkg = w3.pm.get_package("owned", "1.0.0") - assert isinstance(pkg, Package) - assert pkg.name == "owned" diff --git a/tests/core/tools/pytest_ethereum/assets/greeter.json b/tests/core/tools/pytest_ethereum/assets/greeter.json deleted file mode 100644 index 355d8e657e..0000000000 --- a/tests/core/tools/pytest_ethereum/assets/greeter.json +++ /dev/null @@ -1 +0,0 @@ -{"contractTypes":{"greeter":{"abi":[{"constant":false,"inputs":[],"name":"__init__","outputs":[],"payable":false,"type":"constructor"},{"constant":false,"gas":70954,"inputs":[{"name":"x","type":"bytes"}],"name":"setGreeting","outputs":[],"payable":false,"type":"function"},{"constant":false,"gas":4486,"inputs":[],"name":"greet","outputs":[{"name":"out","type":"bytes"}],"payable":false,"type":"function"}],"deploymentBytecode":{"bytecode":"0x600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a052341561009e57600080fd5b6005610140527f48656c6c6f0000000000000000000000000000000000000000000000000000006101605261014080600060c052602060c020602082510161012060006002818352015b826101205160200211156100fb5761011d565b61012051602002850151610120518501555b81516001018083528114156100e8575b50505050505061032f56600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a05263b8e46d3a600051141561013057602060046101403734156100b457600080fd5b60346004356004016101603760146004356004013511156100d457600080fd5b61016080600060c052602060c020602082510161012060006002818352015b8261012051602002111561010657610128565b61012051602002850151610120518501555b81516001018083528114156100f3575b505050505050005b63cfae3217600051141561020157341561014957600080fd5b60008060c052602060c020610180602082540161012060006002818352015b8261012051602002111561017b5761019d565b61012051850154610120516020028501525b8151600101808352811415610168575b5050505050506101e0610180516014818352015b60146101e05111156101c2576101de565b60006101e0516101a001535b81516001018083528114156101b1575b50506020610160526040610180510160206001820306601f8201039050610160f3005b60006000fd5b61012861032f0361012860003961012861032f036000f3"}}},"deployments":{"blockchain://0817570684a8b349c2e15aa1a5ba48d269335d487bcf0d7a2f3ef1f9f764d5e4/block/03d7c71f9f8d8c29a17c00a0d5ce262ad243a575ce5023c430e58dbb02342901":{"greeter":{"address":"0xf2e246bb76df876cef8b38ae84130f4f55de395b","block":"0x03d7c71f9f8d8c29a17c00a0d5ce262ad243a575ce5023c430e58dbb02342901","contractType":"greeter","transaction":"0x56e0b0d02c8f11e62937101ec419899b4f4a8be8ceec825eb9afb797f79e7262"}}},"manifest":"ethpm/3","name":"greeter","sources":{"./contracts/greeter.vy":{"content":"# Vyper Greeter Contract\n\ngreeting: bytes[20]\n\n\n@public\ndef __init__():\n self.greeting = \"Hello\"\n\n\n@public\ndef setGreeting(x: bytes[20]):\n self.greeting = x\n\n\n@public\ndef greet() -> bytes[40]:\n return self.greeting\n"}},"version":"1.0.0"} diff --git a/tests/core/tools/pytest_ethereum/conftest.py b/tests/core/tools/pytest_ethereum/conftest.py deleted file mode 100644 index 7bc300882f..0000000000 --- a/tests/core/tools/pytest_ethereum/conftest.py +++ /dev/null @@ -1,34 +0,0 @@ -from pathlib import ( - Path, -) -import pytest - -from ethpm import ( - get_ethpm_spec_dir, -) -from web3 import ( - Web3, -) - -PYTEST_ETH_TESTS_DIR = Path(__file__).parent - - -@pytest.fixture -def pte_assets_dir(): - return PYTEST_ETH_TESTS_DIR / "assets" - - -@pytest.fixture -def w3(): - return Web3(Web3.EthereumTesterProvider()) - - -@pytest.fixture -def ethpm_spec_dir(): - return get_ethpm_spec_dir() - - -@pytest.fixture -def escrow_deployer(deployer, ethpm_spec_dir): - escrow_manifest_path = ethpm_spec_dir / "examples" / "escrow" / "v3.json" - return deployer(escrow_manifest_path) diff --git a/tests/core/tools/pytest_ethereum/test_deployer.py b/tests/core/tools/pytest_ethereum/test_deployer.py deleted file mode 100644 index 1f5ccbef8b..0000000000 --- a/tests/core/tools/pytest_ethereum/test_deployer.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging -import pytest - -from eth_utils import ( - is_address, -) - -from ethpm import ( - ASSETS_DIR, -) -import web3 -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) - -logging.getLogger("evm").setLevel(logging.INFO) - - -# -# Vyper Contracts -# - - -# User Code -@pytest.fixture -def greeter(deployer, pte_assets_dir): - return deployer(pte_assets_dir / "greeter.json").deploy("greeter") - - -def test_user_code_with_fixture(greeter): - greeter_instance = greeter.deployments.get_instance("greeter") - assert isinstance(greeter_instance, web3.contract.Contract) - greeting = greeter_instance.functions.greet().call() - assert greeting == b"Hello" - - -# -# Solidity Compiler Output -# - - -# SIMPLE -@pytest.fixture -def owned(deployer): - owned_manifest_path = ASSETS_DIR / "owned" / "with_contract_type_v3.json" - owned_deployer = deployer(path=owned_manifest_path) - return owned_deployer.deploy("Owned") - - -def test_owned_deployer(owned): - owned_contract_instance = owned.deployments.get_instance("Owned") - assert is_address(owned_contract_instance.address) - - -# CONSTRUCTOR ARGS -@pytest.fixture -def standard_token(deployer): - standard_token_manifest_path = ( - ASSETS_DIR / "standard-token" / "with_bytecode_v3.json" - ) - standard_token_deployer = deployer(standard_token_manifest_path) - return standard_token_deployer.deploy("StandardToken", 100) - - -def test_standard_token_deployer(standard_token): - standard_token_instance = standard_token.deployments.get_instance("StandardToken") - assert standard_token_instance.functions.totalSupply().call() == 100 - - -# LIBRARY -@pytest.fixture -def safe_math(deployer, ethpm_spec_dir): - safe_math_manifest_path = ethpm_spec_dir / "examples" / "safe-math-lib" / "v3.json" - safe_math_deployer = deployer(safe_math_manifest_path) - return safe_math_deployer.deploy("SafeMathLib") - - -def test_safe_math_deployer(safe_math): - safe_math_instance = safe_math.deployments.get_instance("SafeMathLib") - assert is_address(safe_math_instance.address) - - -def test_escrow_deployer_unlinked(escrow_deployer): - with pytest.raises(DeployerError): - escrow_deployer.deploy("Escrow", escrow_deployer.package.w3.eth.accounts[0]) diff --git a/tests/core/tools/pytest_ethereum/test_linker.py b/tests/core/tools/pytest_ethereum/test_linker.py deleted file mode 100644 index 0f6ae679a4..0000000000 --- a/tests/core/tools/pytest_ethereum/test_linker.py +++ /dev/null @@ -1,82 +0,0 @@ -import pytest - -from ethpm import ( - ASSETS_DIR, - Package, -) -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - link, - linker, - run_python, -) - - -@pytest.fixture -def escrow_deployer(deployer): - escrow_manifest_path = ASSETS_DIR / "escrow" / "with_bytecode_v3.json" - return deployer(escrow_manifest_path) - - -def test_linker(escrow_deployer, w3): - # todo test multiple links in one type - assert isinstance(escrow_deployer, Deployer) - with pytest.raises(DeployerError): - escrow_deployer.deploy("Escrow") - - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0]), - ) - assert hasattr(escrow_strategy, "__call__") - escrow_deployer.register_strategy("Escrow", escrow_strategy) - linked_escrow_package = escrow_deployer.deploy("Escrow") - assert isinstance(linked_escrow_package, Package) - linked_escrow_factory = linked_escrow_package.get_contract_factory("Escrow") - assert linked_escrow_factory.needs_bytecode_linking is False - - -def test_linker_with_from(escrow_deployer, w3): - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0], transaction={"from": w3.eth.accounts[5]}), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - linked_escrow_package = escrow_deployer.deploy("Escrow") - escrow_instance = linked_escrow_package.deployments.get_instance("Escrow") - assert escrow_instance.functions.sender().call() == w3.eth.accounts[5] - - -def test_linker_with_callback(escrow_deployer, w3): - sender = w3.eth.accounts[0] - recipient = w3.eth.accounts[5] - - def callback_fn(package): - escrow_instance = package.deployments.get_instance("Escrow") - tx_hash = escrow_instance.functions.releaseFunds().transact({"from": sender}) - w3.eth.wait_for_transaction_receipt(tx_hash) - - escrow_strategy = linker( - deploy("SafeSendLib", transaction={"from": sender}), - link("Escrow", "SafeSendLib"), - deploy( - "Escrow", - recipient, - transaction={"from": sender, "value": w3.to_wei("1", "ether")}, - ), - run_python(callback_fn), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - assert w3.eth.get_balance(recipient) == w3.to_wei("1000000", "ether") - linked_escrow_package = escrow_deployer.deploy("Escrow") - escrow_instance = linked_escrow_package.deployments.get_instance("Escrow") - assert escrow_instance.functions.sender().call() == sender - assert w3.eth.get_balance(recipient) == w3.to_wei("1000001", "ether") diff --git a/tests/core/tools/pytest_ethereum/test_linker_utils.py b/tests/core/tools/pytest_ethereum/test_linker_utils.py deleted file mode 100644 index f63556b59c..0000000000 --- a/tests/core/tools/pytest_ethereum/test_linker_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -import pytest - -from eth_utils import ( - remove_0x_prefix, - to_hex, -) -from eth_utils.toolz import ( - assoc, -) - -from ethpm.uri import ( - create_latest_block_uri, -) -from web3.tools.pytest_ethereum._utils import ( - contains_matching_uri, - insert_deployment, - pluck_matching_uri, -) -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) - - -@pytest.fixture -def chain_setup(w3): - old_chain_id = remove_0x_prefix(to_hex(w3.eth.get_block(0)["hash"])) - block_hash = remove_0x_prefix(to_hex(w3.eth.get_block("earliest").hash)) - old_chain_uri = f"blockchain://{old_chain_id}/block/{block_hash}" - match_data = { - old_chain_uri: {"x": "x"}, - f"blockchain://1234/block/{block_hash}": {"x": "x"}, - } - no_match_data = { - f"blockchain://56775ac59d0774e6b603a79c4218efeb5653b99ba0ff14db983bac2662251a8a/block/{block_hash}": { # noqa: E501 - "x": "x" - } - } - return w3, match_data, no_match_data, old_chain_uri - - -def test_pluck_matching_uri(chain_setup): - w3, match_data, no_match_data, old_chain_uri = chain_setup - - assert pluck_matching_uri(match_data, w3) == old_chain_uri - with pytest.raises(LinkerError): - assert pluck_matching_uri(no_match_data, w3) - - -def test_contains_matching_uri(chain_setup): - w3, match_data, no_match_data, _ = chain_setup - - assert contains_matching_uri(match_data, w3) is True - assert contains_matching_uri(no_match_data, w3) is False - - -def test_insert_deployment(escrow_deployer): - w3 = escrow_deployer.package.w3 - escrow_package = escrow_deployer.package - init_deployment_data = { - "contract_type": "Escrow", - "address": "0x", - "transaction": "0x", - "block": "0x", - } - new_deployment_data = { - "contract_type": "Escrow", - "address": "0x123", - "transaction": "0x123", - "block": "0x123", - } - w3.testing.mine(1) - init_block_uri = create_latest_block_uri(w3, 0) - alt_block_uri = init_block_uri[:15] + "yxz123" + init_block_uri[21:] - init_block_deployment_data = { - init_block_uri: {"Other": {"x": "x"}, "Escrow": init_deployment_data}, - alt_block_uri: {"alt": {"x": "x"}}, - } - w3.testing.mine(1) - new_block_uri = create_latest_block_uri(w3, 0) - escrow_package.manifest = assoc( - escrow_package.manifest, "deployments", init_block_deployment_data - ) - updated_manifest = insert_deployment( - escrow_package, "Escrow", new_deployment_data, new_block_uri - ) - expected_deployments_data = { - new_block_uri: {"Other": {"x": "x"}, "Escrow": new_deployment_data}, - alt_block_uri: {"alt": {"x": "x"}}, - } - assert updated_manifest["deployments"] == expected_deployments_data diff --git a/tests/ethpm/_utils/test_backend_utils.py b/tests/ethpm/_utils/test_backend_utils.py deleted file mode 100644 index 9587a63cb0..0000000000 --- a/tests/ethpm/_utils/test_backend_utils.py +++ /dev/null @@ -1,88 +0,0 @@ -import os -import pytest - -from ethpm._utils.backend import ( - get_resolvable_backends_for_uri, - get_translatable_backends_for_uri, -) -from ethpm.backends.ipfs import ( - InfuraIPFSBackend, - LocalIPFSBackend, -) -from ethpm.backends.registry import ( - RegistryURIBackend, -) -from ethpm.exceptions import ( - CannotHandleURI, -) -from ethpm.uri import ( - resolve_uri_contents, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.mark.parametrize( - "uri,backends", - ( - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - (InfuraIPFSBackend, LocalIPFSBackend), - ), - ("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", ()), - ), -) -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_get_resolvable_backends_for_supported_uris(dummy_ipfs_backend, uri, backends): - good_backends = get_resolvable_backends_for_uri(uri) - assert good_backends == backends - - -@pytest.mark.parametrize( - "uri,backends", - ( - ( - "erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", - (RegistryURIBackend,), - ), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", ()), - ), -) -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_get_translatable_backends_for_supported_uris( - dummy_ipfs_backend, uri, backends -): - good_backends = get_translatable_backends_for_uri(uri) - assert good_backends == backends - - -@pytest.mark.parametrize( - "uri", - ( - "xxx", - # filesystem - "file:///path_to_erc20.json", - # invalid registry URI scheme - "erc1128://packages.zeppelinos.eth:1/erc20/v1.0.0", - # swarm - "bzz://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - "bzz-immutable:://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", # noqa: E501 - "bzz-raw://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - # internet - "http://github.com/ethpm/ethpm-spec/examples/owned/1.0.0.json#content_hash", - "https://github.com/ethpm/ethpm-spec/examples/owned/1.0.0.json#content_hash", - ), -) -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_resolve_uri_contents_raises_exception_for_unsupported_schemes(uri): - with pytest.raises(CannotHandleURI): - resolve_uri_contents(uri) diff --git a/tests/ethpm/_utils/test_cache_utils.py b/tests/ethpm/_utils/test_cache_utils.py deleted file mode 100644 index 9ba49de7d3..0000000000 --- a/tests/ethpm/_utils/test_cache_utils.py +++ /dev/null @@ -1,20 +0,0 @@ -import random - -from ethpm._utils.cache import ( - cached_property, -) - - -def test_cached_property(): - class FOO: - def __int__(self): - pass - - @cached_property - def generate_number(self): - return random.random() - - foo = FOO() - foo_number = foo.generate_number - foo_cached_number = foo.generate_number - assert foo_number == foo_cached_number diff --git a/tests/ethpm/_utils/test_chain_utils.py b/tests/ethpm/_utils/test_chain_utils.py deleted file mode 100644 index 2cc00efdcb..0000000000 --- a/tests/ethpm/_utils/test_chain_utils.py +++ /dev/null @@ -1,61 +0,0 @@ -import pytest - -from ethpm._utils.chains import ( - is_BIP122_block_uri, - is_supported_chain_id, - parse_BIP122_uri, -) - -HASH_A = "0x1234567890123456789012345678901234567890123456789012345678901234" -HASH_A_NO_PREFIX = "1234567890123456789012345678901234567890123456789012345678901234" -HASH_B = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" -HASH_B_NO_PREFIX = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" -BLOCK_URI = f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B_NO_PREFIX}" -TRANSACTION_URI = f"blockchain://{HASH_A_NO_PREFIX}/transaction/{HASH_B_NO_PREFIX}" - - -@pytest.mark.parametrize( - "value,expected", - ( - (BLOCK_URI, True), - (TRANSACTION_URI, False), - (f"blockchain://{HASH_A}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B}", False), - (f"blockchain://{HASH_A}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX[:-1]}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B_NO_PREFIX[:-1]}", False), - ), -) -def test_is_BIP122_block_uri(value, expected): - actual = is_BIP122_block_uri(value) - assert actual is expected - - -@pytest.mark.parametrize( - "value, expected_resource_type", - ((TRANSACTION_URI, "transaction"), (BLOCK_URI, "block")), -) -def test_parse_BIP122_uri(value, expected_resource_type): - chain_id, resource_type, resource_identifier = parse_BIP122_uri(value) - assert chain_id == HASH_A - assert resource_type == expected_resource_type - assert resource_identifier == HASH_B - - -@pytest.mark.parametrize( - "chain_id,expected", - ( - (1, True), - (5, True), - (11155111, True), - (2, False), - (4, False), - ("1", False), - ({}, False), - (None, False), - (False, False), - ), -) -def test_is_supported_chain_id(chain_id, expected): - actual = is_supported_chain_id(chain_id) - assert actual is expected diff --git a/tests/ethpm/_utils/test_contract_utils.py b/tests/ethpm/_utils/test_contract_utils.py deleted file mode 100644 index a6bd9f00e2..0000000000 --- a/tests/ethpm/_utils/test_contract_utils.py +++ /dev/null @@ -1,93 +0,0 @@ -import pytest - -from ethpm._utils.contract import ( - generate_contract_factory_kwargs, -) -from ethpm.exceptions import ( - EthPMValidationError, - InsufficientAssetsError, -) -from ethpm.validation.misc import ( - validate_w3_instance, -) -from ethpm.validation.package import ( - validate_contract_name, - validate_minimal_contract_factory_data, -) - - -@pytest.mark.parametrize( - "contract_data", - ( - {"abi": "", "deploymentBytecode": ""}, - { - "abi": "", - "deploymentBytecode": {"bytecode": ""}, - "runtimeBytecode": {"bytecode": ""}, - }, - ), -) -def test_validate_minimal_contract_factory_data_validates(contract_data): - assert validate_minimal_contract_factory_data(contract_data) is None - - -@pytest.mark.parametrize( - "contract_data", - ( - {"abi": ""}, - {"deploymentBytecode": {"bytecode": ""}}, - {"runtimeBytecode": {"bytecode": ""}, "other": ""}, - ), -) -def test_validate_minimal_contract_factory_data_invalidates(contract_data): - with pytest.raises(InsufficientAssetsError): - validate_minimal_contract_factory_data(contract_data) - - -@pytest.mark.parametrize("name", ("A1", "A-1", "A_1", "X" * 256)) -def test_validate_contract_name_validates(name): - assert validate_contract_name(name) is None - - -@pytest.mark.parametrize("name", ("", "-abc", "A=bc", "X" * 257)) -def test_validate_contract_name_invalidates(name): - with pytest.raises(EthPMValidationError): - assert validate_contract_name(name) - - -@pytest.mark.parametrize( - "contract_data,expected_kwargs", - ( - ({"abi": ""}, {"abi"}), - ({"deploymentBytecode": {"bytecode": ""}}, {"bytecode"}), - ( - {"abi": "", "runtimeBytecode": {"bytecode": ""}}, - {"abi", "bytecode_runtime"}, - ), - ( - { - "abi": "", - "deploymentBytecode": { - "bytecode": "", - "linkReferences": [ - {"offsets": [402, 639], "length": 20, "name": "SafeSendLib"} - ], - }, - }, - {"abi", "bytecode", "unlinked_references"}, - ), - ), -) -def test_generate_contract_factory_kwargs(contract_data, expected_kwargs): - contract_factory = generate_contract_factory_kwargs(contract_data) - assert contract_factory.keys() == expected_kwargs - - -def test_validate_w3_instance_validates(w3): - assert validate_w3_instance(w3) is None - - -@pytest.mark.parametrize("w3", ("NotWeb3", b"NotWeb3", 1234)) -def test_validate_w3_instance_invalidates(w3): - with pytest.raises(ValueError): - assert validate_w3_instance(w3) diff --git a/tests/ethpm/_utils/test_ipfs_utils.py b/tests/ethpm/_utils/test_ipfs_utils.py deleted file mode 100644 index 53bd0c90a7..0000000000 --- a/tests/ethpm/_utils/test_ipfs_utils.py +++ /dev/null @@ -1,122 +0,0 @@ -from pathlib import ( - Path, -) -import pytest - -from ethpm._utils.ipfs import ( - extract_ipfs_path_from_uri, - generate_file_hash, - is_ipfs_uri, -) - - -@pytest.mark.parametrize( - "value,expected", - ( - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ), -) -def test_extract_ipfs_path_from_uri(value, expected): - actual = extract_ipfs_path_from_uri(value) - assert actual == expected - - -@pytest.mark.parametrize( - "value,expected", - ( - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - # malformed - ("ipfs//QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - ("ipfs/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - ("ipfsQmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - # HTTP - ("http://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", False), - ("https://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", False), - # No hash - ("ipfs://", False), - ), -) -def test_is_ipfs_uri(value, expected): - actual = is_ipfs_uri(value) - assert actual is expected - - -@pytest.mark.parametrize( - "file_name,file_contents,expected", - ( - ("test-1.txt", "piper\n", "QmUdxEGxvp71kqYLkA91mtNg9QRRSPBtA3UV6VuYhoP7DB"), - ( - "test-2.txt", - "pipermerriam\n", - "QmXqrQR7EMePe9LCRUVrfkxYg5EHRNpcA1PZnN4AnbM9DW", - ), - ( - "test-3.txt", - "this is a test file for ipfs hash generation\n", - "QmYknNUKXWSaxfCWVgHd8uVCYHhzPerVCLvCCBedWtqbnv", - ), - ), -) -def test_generate_file_hash(tmpdir, file_name, file_contents, expected): - p = tmpdir.mkdir("sub").join(file_name) - p.write(file_contents) - ipfs_multihash = generate_file_hash(Path(p).read_bytes()) - assert ipfs_multihash == expected diff --git a/tests/ethpm/_utils/test_registry_utils.py b/tests/ethpm/_utils/test_registry_utils.py deleted file mode 100644 index e2b346fef8..0000000000 --- a/tests/ethpm/_utils/test_registry_utils.py +++ /dev/null @@ -1,86 +0,0 @@ -import pytest - -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.uri import ( - validate_registry_uri, -) - - -@pytest.mark.parametrize( - "uri", - ( - # no package id in uri - ("erc1319://zeppelinos.eth"), - ("erc1319://zeppelinos.eth:1"), - ("erc1319://zeppelinos.eth:1/"), - ("erc1319://packages.zeppelinos.eth"), - ("erc1319://packages.zeppelinos.eth:1"), - ("erc1319://packages.zeppelinos.eth:1/"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/"), - # with package id in uri - ("erc1319://zeppelinos.eth/erc20/"), - ("erc1319://zeppelinos.eth:1/erc20/"), - ("erc1319://zeppelinos.eth:1/erc20//"), - ("erc1319://zeppelinos.eth/erc20@1.0.0"), - ("erc1319://zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319://zeppelinos.eth:1/erc20@1.0.0/"), - ("erc1319://packages.zeppelinos.eth/erc20@"), - ("erc1319://packages.zeppelinos.eth:1/erc20@"), - ("erc1319://packages.zeppelinos.eth:1/erc20@/"), - ("erc1319://packages.zeppelinos.eth/erc20@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/erc20@1.0.0/"), - ("erc1319://packages.ethereum.eth/greeter@%3E%3D1.0.2%2C%3C2"), - ("erc1319://packages.ethereum.eth:1/greeter@%3E%3D1.0.2%2C%3C2"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601/erc20@1.0.0"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20@1.0.0"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20@1.0.0/"), - ( - "erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20@1.0.0/deployments/ERC139" # noqa: E501 - ), - ), -) -def test_is_registry_uri_validates(uri): - assert validate_registry_uri(uri) is None - - -@pytest.mark.parametrize( - "uri", - ( - # invalid authority - ("erc1319://zeppelinos.eth:333/erc20@1.0.0"), - ("erc1319://packages.zeppelinos.com:1/erc20@1.0.0"), - ("erc1319://package.manager.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319://packageszeppelinoseth:1/erc20@1.0.0"), - ("erc1319://0xd3cda913deb6f67967b99d67acdfa1712c293601:1/erc20@1.0.0"), - # invalid package name - ("erc1319://packages.zeppelinos.eth/@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/@1.0.0/"), - ("erc1319://packages.zeppelinos.eth/!rc20?@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/!rc20?@1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/!rc20?@1.0.0/"), - # malformed - ("erc1319packageszeppelinosetherc20@1.0.0"), - ("erc1319:packages.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319:packages.zeppelinos.eth:1/erc20@1.0.0/"), - ("erc1319:/packages.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319:/packages.zeppelinos.eth:1/erc20@1.0.0/"), - ("erc1319/packages.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319//packages.zeppelinos.eth:1/erc20@1.0.0"), - ("erc1319packages.zeppelinos.eth:1/erc20@1.0.0"), - # wrong scheme - ("http://packages.zeppelinos.eth:1/erc20@1.0.0"), - ("ercXX://packages.zeppelinos.eth:1/erc20@1.0.0"), - # no path - ("erc1319://"), - ("1234"), - ), -) -def test_is_registry_uri_raises_exception_for_invalid_uris(uri): - with pytest.raises(EthPMValidationError): - validate_registry_uri(uri) diff --git a/tests/ethpm/backends/test_http_backends.py b/tests/ethpm/backends/test_http_backends.py deleted file mode 100644 index 0f34f30f93..0000000000 --- a/tests/ethpm/backends/test_http_backends.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import pytest - -from requests.exceptions import ( - HTTPError, -) - -from ethpm import ( - Package, -) -from ethpm.backends.http import ( - GithubOverHTTPSBackend, -) -from ethpm.constants import ( - GITHUB_API_AUTHORITY, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.mark.parametrize( - "uri", - ( - "https://api.github.com/repos/ethpm/ethpm-spec/git/blobs/899042f95ad624d5ecf0b96b0926c96cd682522d", # noqa: E501 - ), -) -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_github_over_https_backend_fetch_uri_contents(uri, owned_contract, w3): - # these tests may occasionally fail CI as a result of their network requests - backend = GithubOverHTTPSBackend() - assert backend.base_uri == GITHUB_API_AUTHORITY - # integration with Package.from_uri - owned_package = Package.from_uri(uri, w3) - assert owned_package.name == "owned" - - -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_github_over_https_backend_raises_error_with_invalid_content_hash(w3): - invalid_uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03123" # noqa: E501 - with pytest.raises(HTTPError): - Package.from_uri(invalid_uri, w3) diff --git a/tests/ethpm/backends/test_ipfs_backends.py b/tests/ethpm/backends/test_ipfs_backends.py deleted file mode 100644 index 2a9c668b50..0000000000 --- a/tests/ethpm/backends/test_ipfs_backends.py +++ /dev/null @@ -1,138 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest - -from eth_utils import ( - to_text, -) -from ipfshttpclient.exceptions import ( - TimeoutError, -) - -from ethpm.backends.ipfs import ( - DummyIPFSBackend, - InfuraIPFSBackend, - LocalIPFSBackend, - get_ipfs_backend, - get_ipfs_backend_class, -) -from ethpm.constants import ( - INFURA_GATEWAY_MULTIADDR, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.fixture -def owned_manifest_path(ethpm_spec_dir): - return ethpm_spec_dir / "examples" / "owned" / "v3.json" - - -@pytest.fixture -def fake_client(owned_manifest_path): - class FakeClient: - def cat(self, ipfs_hash): - return ipfs_hash - - def add(self, file_or_dir_path, recursive): - if Path(file_or_dir_path) == owned_manifest_path: - return { - "Hash": "QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW", - "Name": "1.0.0.json", - "Size": "454", - } - - return FakeClient() - - -@pytest.mark.parametrize( - "base_uri,backend", ((INFURA_GATEWAY_MULTIADDR, InfuraIPFSBackend()),) -) -def test_ipfs_and_infura_gateway_backends_fetch_uri_contents(base_uri, backend): - uri = "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - assert backend.base_uri == base_uri - contents = backend.fetch_uri_contents(uri) - assert contents.startswith(b"pragma solidity") - - -@pytest.mark.xfail(strict=False, raises=TimeoutError) -def test_local_ipfs_backend(owned_manifest_path): - uri = "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - backend = LocalIPFSBackend() - backend.pin_assets(owned_manifest_path.parent / "contracts" / "Owned.sol") - contents = backend.fetch_uri_contents(uri) - assert contents.startswith(b"pragma solidity") - - -@pytest.mark.parametrize( - "uri,expected", - ( - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("http://raw.githubusercontent.com/ethpm/py-ethpm#0x123", False), - ("https://raw.githubusercontent.com/ethpm/py-ethpm#0x123", False), - ( - "bzz://679bde3ccb6fb911db96a0ea1586c04899c6c0cc6d3426e9ee361137b270a463", - False, - ), - ("ercxxx://packages.eth/owned?version=1.0.0", False), - ), -) -def test_base_ipfs_gateway_backend_correctly_handles_uri_schemes(uri, expected): - backend = InfuraIPFSBackend() - assert backend.can_resolve_uri(uri) is expected - - -def test_dummy_ipfs_backend(): - pkg = DummyIPFSBackend().fetch_uri_contents( - "ipfs://QmQNffBrmbB3TuBCtYfYsJWJVLssatWXa3H6CkGeyNUySA" - ) - manifest = json.loads(to_text(pkg)) - assert manifest["name"] == "standard-token" - - -def test_get_ipfs_backend_class_with_default_backend(): - backend = get_ipfs_backend_class() - assert issubclass(backend, InfuraIPFSBackend) - - -def test_get_ipfs_backend_with_default_backend(): - backend = get_ipfs_backend() - assert isinstance(backend, InfuraIPFSBackend) - - -def test_get_uri_backend_with_env_variable(dummy_ipfs_backend, monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.LocalIPFSBackend" - ) - backend = get_ipfs_backend() - assert isinstance(backend, LocalIPFSBackend) - - -def test_pin_assets_to_dummy_backend( - dummy_ipfs_backend, ethpm_spec_dir, owned_manifest_path -): - # Test pinning a file - backend = get_ipfs_backend() - hashes = backend.pin_assets(owned_manifest_path) - asset_data = hashes[0] - assert asset_data["Name"] == "v3.json" - assert asset_data["Hash"] == "QmcxvhkJJVpbxEAa6cgW3B6XwPJb79w9GpNUv2P2THUzZR" - assert asset_data["Size"] == "478" - # Test pinning a directory - dir_data = backend.pin_assets( - ethpm_spec_dir / "examples" / "standard-token" / "contracts" - ) - dir_names = [result["Name"] for result in dir_data] - dir_hashes = [result["Hash"] for result in dir_data] - dir_sizes = [result["Size"] for result in dir_data] - assert len(dir_data) == 2 - assert "StandardToken.sol" in dir_names - assert "QmUofKBtNJVaqoSAtnHfrarJyyLm1oMUTAK4yCtnmYMJVy" in dir_hashes - assert "2949" in dir_sizes diff --git a/tests/ethpm/backends/test_registry_backend.py b/tests/ethpm/backends/test_registry_backend.py deleted file mode 100644 index a17adcd724..0000000000 --- a/tests/ethpm/backends/test_registry_backend.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import pytest - -from ethpm.backends.registry import ( - RegistryURIBackend, - parse_registry_uri, -) -from ethpm.exceptions import ( - CannotHandleURI, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.fixture -def backend(): - return RegistryURIBackend() - - -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_registry_uri_backend(backend): - valid_uri = "erc1319://0x1457890158DECD360e6d4d979edBcDD59c35feeB:1/owned@1.0.0" - expected_uri = "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - assert backend.can_translate_uri(valid_uri) is True - assert backend.can_resolve_uri(valid_uri) is False - assert backend.fetch_uri_contents(valid_uri) == expected_uri - - -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_registry_uri_supports_ens_domains(backend): - valid_uri = "erc1319://defi.snakecharmers.eth:1/compound@1.0.0" - parsed = parse_registry_uri(valid_uri) - expected_uri = "ipfs://QmYvsyuxjj9mKmCvn3jrdfnaHYwFsyHXUu7kETrN4dBhE6" - assert backend.can_translate_uri(valid_uri) is True - assert backend.can_resolve_uri(valid_uri) is False - assert backend.fetch_uri_contents(valid_uri) == expected_uri - assert parsed.address == "0xA635F17288187daE5b424D343E21FF44a79ce922" - assert parsed.ens == "defi.snakecharmers.eth" - assert parsed.chain_id == "1" - assert parsed.name == "compound" - assert parsed.version == "1.0.0" - - -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_registry_uri_backend_raises_exception_for_non_mainnet_chains(backend): - ropsten_uri = "erc1319://snakecharmers.eth:3/owned@1.0.0" - with pytest.raises(CannotHandleURI, match="Currently only mainnet"): - backend.fetch_uri_contents(ropsten_uri) diff --git a/tests/ethpm/conftest.py b/tests/ethpm/conftest.py deleted file mode 100644 index b137b97cf4..0000000000 --- a/tests/ethpm/conftest.py +++ /dev/null @@ -1,270 +0,0 @@ -import copy -import pytest - -from eth_utils.toolz import ( - assoc_in, -) - -from ethpm import ( - ASSETS_DIR, - Package, - get_ethpm_spec_dir, -) -from ethpm._utils.chains import ( - create_block_uri, -) -from ethpm.tools import ( - get_ethpm_spec_manifest, -) -from ethpm.uri import ( - create_latest_block_uri, -) -from web3 import ( - Web3, -) -from web3.tools import ( - linker, -) - -V3_PACKAGE_NAMES = [ - ("escrow", "v3.json"), - ("owned", "v3.json"), - ("piper-coin", "v3.json"), - ("safe-math-lib", "v3.json"), - ("standard-token", "v3.json"), - ("transferable", "v3.json"), - ("wallet-with-send", "v3.json"), - ("wallet", "v3.json"), -] - - -def pytest_addoption(parser): - parser.addoption("--integration", action="store_true", default=False) - - -@pytest.fixture -def ethpm_spec_dir(): - return get_ethpm_spec_dir() - - -@pytest.fixture(params=V3_PACKAGE_NAMES) -def all_strict_manifests(request): - return (fetch_manifest_path(request.param[0], "v3.json")).read_text().rstrip("\n") - - -@pytest.fixture(params=V3_PACKAGE_NAMES) -def all_pretty_manifests(request): - return ( - (fetch_manifest_path(request.param[0], "v3-pretty.json")) - .read_text() - .rstrip("\n") - ) - - -def fetch_manifest(name, version): - return get_ethpm_spec_manifest(name, version) - - -def fetch_manifest_path(name, version): - ethpm_spec_dir = get_ethpm_spec_dir() - return ethpm_spec_dir / "examples" / name / version - - -MANIFESTS_V3 = { - name: fetch_manifest(name, version) for name, version in V3_PACKAGE_NAMES -} - - -@pytest.fixture -def w3(): - w3 = Web3(Web3.EthereumTesterProvider()) - w3.eth.default_account = w3.eth.accounts[0] - return w3 - - -@pytest.fixture -def dummy_ipfs_backend(monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - - -@pytest.fixture -def get_manifest(): - def _get_manifest(name): - return copy.deepcopy(MANIFESTS_V3[name]) - - return _get_manifest - - -@pytest.fixture(params=V3_PACKAGE_NAMES) -def all_manifests(request, get_manifest): - return get_manifest(request.param[0]) - - -# safe-math-lib currently used as default manifest for testing -# should be extended to all_manifest_types asap -@pytest.fixture -def safe_math_manifest(get_manifest): - return get_manifest("safe-math-lib") - - -@pytest.fixture -def piper_coin_manifest(): - return get_ethpm_spec_manifest("piper-coin", "v3.json") - - -ESCROW_DEPLOYMENT_BYTECODE_V3 = { - "bytecode": "0x60806040526040516020806102a8833981016040525160008054600160a060020a0319908116331790915560018054600160a060020a0390931692909116919091179055610256806100526000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166366d003ac811461005b57806367e404ce1461008c57806369d89575146100a1575b600080fd5b34801561006757600080fd5b506100706100b8565b60408051600160a060020a039092168252519081900360200190f35b34801561009857600080fd5b506100706100c7565b3480156100ad57600080fd5b506100b66100d6565b005b600154600160a060020a031681565b600054600160a060020a031681565b600054600160a060020a031633141561019857600154604080517f9341231c000000000000000000000000000000000000000000000000000000008152600160a060020a039092166004830152303160248301525173000000000000000000000000000000000000000091639341231c916044808301926020929190829003018186803b15801561016657600080fd5b505af415801561017a573d6000803e3d6000fd5b505050506040513d602081101561019057600080fd5b506102289050565b600154600160a060020a031633141561005657600054604080517f9341231c000000000000000000000000000000000000000000000000000000008152600160a060020a039092166004830152303160248301525173000000000000000000000000000000000000000091639341231c916044808301926020929190829003018186803b15801561016657600080fd5b5600a165627a7a723058201766d3411ff91d047cf900369478c682a497a6e560cd1b2fe4d9f2d6fe13b4210029", # noqa: E501 - "linkReferences": [{"offsets": [383, 577], "length": 20, "name": "SafeSendLib"}], -} - - -@pytest.fixture -def escrow_manifest(get_manifest): - escrow_manifest = get_manifest("escrow") - escrow_manifest["contractTypes"]["Escrow"][ - "deploymentBytecode" - ] = ESCROW_DEPLOYMENT_BYTECODE_V3 - return escrow_manifest - - -@pytest.fixture -def get_factory(get_manifest, escrow_manifest, w3): - def _get_factory(package, factory_name): - manifest = get_manifest(package) - # Special case to fetch escrow manifest with added deployment bytecode - if package == "escrow": - manifest = escrow_manifest - Pkg = Package(manifest, w3) - factory = Pkg.get_contract_factory(factory_name) - return factory - - return _get_factory - - -@pytest.fixture -def owned_contract(ethpm_spec_dir): - return ( - ethpm_spec_dir / "examples" / "owned" / "contracts" / "Owned.sol" - ).read_text() - - -@pytest.fixture -def invalid_manifest(safe_math_manifest): - safe_math_manifest["manifest"] = 1 - return safe_math_manifest - - -@pytest.fixture -def manifest_with_no_deployments(safe_math_manifest): - manifest = copy.deepcopy(safe_math_manifest) - manifest.pop("deployments") - return manifest - - -@pytest.fixture -def manifest_with_empty_deployments(tmpdir, safe_math_manifest): - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"] = {} - return manifest - - -@pytest.fixture -def escrow_package(deployer, w3, ethpm_spec_dir): - escrow_manifest = ethpm_spec_dir / "examples" / "escrow" / "v3.json" - escrow_deployer = deployer(escrow_manifest) - escrow_strategy = linker.linker( - linker.deploy("SafeSendLib"), - linker.link("Escrow", "SafeSendLib"), - linker.deploy("Escrow", w3.eth.accounts[0]), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - return escrow_deployer.deploy("Escrow") - - -@pytest.fixture -def safe_math_lib_package(deployer, w3): - safe_math_lib_manifest = fetch_manifest_path("safe-math-lib", "v3.json") - safe_math_deployer = deployer(safe_math_lib_manifest) - return safe_math_deployer.deploy("SafeMathLib") - - -@pytest.fixture -def safe_math_lib_package_with_alias(deployer, w3): - safe_math_lib_manifest = ( - ASSETS_DIR / "safe-math-lib" / "v3-strict-no-deployments.json" - ) - safe_math_deployer = deployer(safe_math_lib_manifest) - pkg = safe_math_deployer.deploy("SafeMathLib") - blockchain_uri = list(pkg.manifest["deployments"].keys())[0] - deployment_data = pkg.manifest["deployments"][blockchain_uri]["SafeMathLib"] - aliased_manifest = assoc_in( - pkg.manifest, - ["deployments", blockchain_uri], - {"safe-math-lib-alias": deployment_data}, - ) - return Package(aliased_manifest, w3) - - -@pytest.fixture -def manifest_with_no_matching_deployments(w3, tmpdir, safe_math_manifest): - w3.testing.mine(5) - incorrect_genesis_hash = b"\x00" * 31 + b"\x01" - block = w3.eth.get_block("earliest") - block_uri = create_block_uri( - w3.to_hex(incorrect_genesis_hash), w3.to_hex(block.hash) - ) - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][block_uri] = { - "SafeMathLib": { - "contractType": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", # noqa: E501 - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", # noqa: E501 - } - } - return manifest - - -@pytest.fixture -def manifest_with_multiple_matches(w3, tmpdir, safe_math_manifest): - w3.testing.mine(5) - block_uri = create_latest_block_uri(w3, from_blocks_ago=0) - w3.testing.mine(1) - second_block_uri = create_latest_block_uri(w3, from_blocks_ago=0) - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][block_uri] = { - "SafeMathLib": { - "contractType": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", # noqa: E501 - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", # noqa: E501 - } - } - manifest["deployments"][second_block_uri] = { - "SafeMathLib": { - "contractType": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", # noqa: E501 - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", # noqa: E501 - } - } - return manifest - - -@pytest.fixture -def manifest_with_conflicting_deployments(tmpdir, safe_math_manifest): - # two different blockchain uri's representing the same chain - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][ - "blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/1e96de11320c83cca02e8b9caf3e489497e8e432befe5379f2f08599f8aecede" # noqa: E501 - ] = { - "WrongNameLib": { - "contractType": "WrongNameLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", # noqa: E501 - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", # noqa: E501 - } - } - return manifest diff --git a/tests/ethpm/integration/test_escrow_manifest.py b/tests/ethpm/integration/test_escrow_manifest.py deleted file mode 100644 index 15d13e29c5..0000000000 --- a/tests/ethpm/integration/test_escrow_manifest.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest - -from eth_utils import ( - to_canonical_address, -) - -from ethpm import ( - Package, -) -from ethpm.exceptions import ( - BytecodeLinkingError, -) -from ethpm.tools import ( - get_ethpm_spec_manifest, -) -import web3 - - -def test_deployed_escrow_and_safe_send(escrow_manifest, w3): - # Deploy a SafeSendLib - safe_send_manifest = get_ethpm_spec_manifest("escrow", "v3.json") - safe_send_contract_type = safe_send_manifest["contractTypes"]["SafeSendLib"] - SafeSend = w3.eth.contract( - abi=safe_send_contract_type["abi"], - bytecode=safe_send_contract_type["deploymentBytecode"]["bytecode"], - ) - tx_hash = SafeSend.constructor().transact() - tx_receipt = w3.eth.get_transaction_receipt(tx_hash) - safe_send_address = to_canonical_address(tx_receipt["contractAddress"]) - - EscrowPackage = Package(escrow_manifest, w3) - EscrowFactory = EscrowPackage.get_contract_factory("Escrow") - LinkedEscrowFactory = EscrowFactory.link_bytecode( - {"SafeSendLib": safe_send_address} - ) - - # Deploy an Escrow Contract - escrow_tx_hash = LinkedEscrowFactory.constructor( - "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" - ).transact() - escrow_tx_receipt = w3.eth.wait_for_transaction_receipt(escrow_tx_hash) - escrow_address = escrow_tx_receipt.contractAddress - - # Cannot deploy with an unlinked factory - with pytest.raises(BytecodeLinkingError): - escrow_tx_hash = EscrowFactory.constructor( - "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" - ).transact() - - # Cannot instantiate a contract instance from an unlinked factory - with pytest.raises(BytecodeLinkingError): - EscrowFactory(escrow_address) - contract_instance = LinkedEscrowFactory(escrow_address) - assert EscrowFactory.needs_bytecode_linking is True - assert LinkedEscrowFactory.needs_bytecode_linking is False - assert isinstance(contract_instance, web3.contract.Contract) - assert safe_send_address in LinkedEscrowFactory.bytecode - assert safe_send_address in LinkedEscrowFactory.bytecode_runtime - assert safe_send_address not in EscrowFactory.bytecode - assert safe_send_address not in EscrowFactory.bytecode_runtime diff --git a/tests/ethpm/integration/test_ipfs_integration.py b/tests/ethpm/integration/test_ipfs_integration.py deleted file mode 100644 index 08b174e2de..0000000000 --- a/tests/ethpm/integration/test_ipfs_integration.py +++ /dev/null @@ -1,70 +0,0 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.backends.ipfs import ( - InfuraIPFSBackend, - LocalIPFSBackend, - get_ipfs_backend, -) -from ethpm.tools import ( - builder as b, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - -OWNED_MANIFEST_PATH = ASSETS_DIR / "owned" / "1.0.0.json" - - -def test_local_ipfs_backend_integration_round_trip(monkeypatch, request): - """ - To run this integration test requires an running IPFS node. - If you want to run these tests, first start your IPFS node, and - then run pytest with the arg `--integration`. - """ - if not request.config.getoption("--integration"): - pytest.skip("Not asked to run integration tests") - - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.LocalIPFSBackend" - ) - backend = get_ipfs_backend() - [asset_data] = backend.pin_assets(OWNED_MANIFEST_PATH) - assert asset_data["Name"] == "1.0.0.json" - assert asset_data["Hash"] == "QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - local_manifest = to_bytes(text=OWNED_MANIFEST_PATH.read_text()) - ipfs_manifest = backend.fetch_uri_contents(asset_data["Hash"]) - assert ipfs_manifest == local_manifest - - -@pytest.fixture(params=[LocalIPFSBackend, InfuraIPFSBackend]) -def backend(request): - return request.param() - - -def test_builder_pins_manifest_to_provided_ipfs_backend(backend, request): - if not request.config.getoption("--integration"): - pytest.skip("Not asked to run integration tests") - - minified_manifest_hash = "QmVwwpt2BAkmWQt4eNnswhWd6bYgLbnUQDMHdVMHotwiqz" - (manifest,) = b.build( - {}, - b.package_name("package"), - b.manifest_version("2"), - b.version("1.0.0"), - b.pin_to_ipfs(backend=backend), - ) - assert manifest["Hash"] == minified_manifest_hash - pinned_manifest = backend.fetch_uri_contents(manifest["Hash"]) - assert ( - pinned_manifest - == b'{"manifest_version":"2","package_name":"package","version":"1.0.0"}' - ) diff --git a/tests/ethpm/test_contract.py b/tests/ethpm/test_contract.py deleted file mode 100644 index 9e4e8d5a28..0000000000 --- a/tests/ethpm/test_contract.py +++ /dev/null @@ -1,182 +0,0 @@ -import pytest - -from eth_utils import ( - to_canonical_address, -) - -from ethpm import ( - Package, -) -from ethpm.contract import ( - LinkableContract, - apply_all_link_refs, -) -from ethpm.exceptions import ( - BytecodeLinkingError, -) -from web3.contract import ( - Contract, -) - - -@pytest.mark.parametrize( - "package,factory,attr_dict", - ( - ( - "escrow", - "Escrow", - {"SafeSendLib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0"}, - ), - ( - "wallet", - "Wallet", - {"safe-math-lib:SafeMathLib": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B"}, - ), - ), -) -def test_linkable_contract_class_handles_link_refs( - package, factory, attr_dict, get_factory, w3 -): - factory = get_factory(package, factory) - assert factory.needs_bytecode_linking is True - linked_factory = factory.link_bytecode(attr_dict) - assert issubclass(LinkableContract, Contract) - assert issubclass(factory, LinkableContract) - assert issubclass(linked_factory, LinkableContract) - assert factory.needs_bytecode_linking is True - assert linked_factory.needs_bytecode_linking is False - # Can't link a factory that's already linked - with pytest.raises(BytecodeLinkingError): - linked_factory.link_bytecode(attr_dict) - offset = factory.unlinked_references[0]["offsets"][0] - link_address = to_canonical_address(list(attr_dict.values())[0]) - # Ignore lint error b/c black conflict - assert factory.bytecode[offset : offset + 20] == b"\00" * 20 # noqa: E203 - assert linked_factory.bytecode[offset : offset + 20] == link_address # noqa: E203 - - -def test_linkable_contract_class_handles_missing_link_refs(get_manifest, w3): - safe_math_manifest = get_manifest("safe-math-lib") - SafeMathLib = Package(safe_math_manifest, w3) - safe_math_lib = SafeMathLib.get_contract_factory("SafeMathLib") - assert safe_math_lib.needs_bytecode_linking is False - with pytest.raises(BytecodeLinkingError): - safe_math_lib.link_bytecode( - {"SafeMathLib": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B"} - ) - - -SAFE_SEND_ADDRESS = "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" -SAFE_MATH_ADDRESS = "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B" -SAFE_SEND_CANON = to_canonical_address(SAFE_SEND_ADDRESS) -SAFE_MATH_CANON = to_canonical_address(SAFE_MATH_ADDRESS) - - -@pytest.mark.parametrize( - "bytecode,link_refs,attr_dict,expected", - ( - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1]}], - {"SafeSendLib": SAFE_SEND_CANON}, - b"\00" + SAFE_SEND_CANON + bytearray(39), - ), - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1, 31]}], - {"SafeSendLib": SAFE_SEND_CANON}, - b"\00" + SAFE_SEND_CANON + bytearray(10) + SAFE_SEND_CANON + bytearray(9), - ), - ( - bytearray(80), - [ - {"length": 20, "name": "SafeSendLib", "offsets": [1, 50]}, - {"length": 20, "name": "SafeMathLib", "offsets": [25]}, - ], - {"SafeSendLib": SAFE_SEND_CANON, "SafeMathLib": SAFE_MATH_CANON}, - b"\00" - + SAFE_SEND_CANON - + bytearray(4) - + SAFE_MATH_CANON - + bytearray(5) - + SAFE_SEND_CANON - + bytearray(10), - ), - ), -) -def test_apply_all_link_refs(bytecode, link_refs, attr_dict, expected): - actual = apply_all_link_refs(bytecode, link_refs, attr_dict) - assert actual == expected - - -@pytest.mark.parametrize( - "bytecode,link_refs,attr_dict", - ( - # Non-empty bytecode - ( - b"\01" * 60, - [{"length": 20, "name": "SafeSendLib", "offsets": [1]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal offset - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [61]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal offsets - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1, 3]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal length - ( - bytearray(60), - [{"length": 61, "name": "SafeSendLib", "offsets": [0]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Conflicting link refs - ( - bytearray(60), - [ - {"length": 20, "name": "SafeSendLib", "offsets": [1]}, - {"length": 20, "name": "SafeMathLib", "offsets": [15]}, - ], - {"SafeSendLib": SAFE_SEND_CANON, "SafeMathLib": SAFE_MATH_CANON}, - ), - ), -) -def test_apply_all_link_refs_with_incorrect_args(bytecode, link_refs, attr_dict): - with pytest.raises(BytecodeLinkingError): - apply_all_link_refs(bytecode, link_refs, attr_dict) - - -@pytest.mark.parametrize( - "attr_dict", - ( - {}, - # invalid address - {"SafeSendLib": "abc"}, - {"SafeSendLib": 123}, - {"SafeSendLib": b"abc"}, - # Non-matching refs - {"safe-send-lib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0"}, - { - "SafeSendLib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0", - "Wallet": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B", - }, - ), -) -def test_contract_factory_invalidates_incorrect_attr_dicts(get_factory, attr_dict): - safe_send = get_factory("escrow", "SafeSendLib") - assert safe_send.needs_bytecode_linking is False - with pytest.raises(BytecodeLinkingError): - safe_send.link_bytecode(attr_dict) - - -def test_unlinked_factory_cannot_be_deployed(get_factory): - escrow = get_factory("escrow", "Escrow") - assert escrow.needs_bytecode_linking - with pytest.raises(BytecodeLinkingError): - escrow.constructor("0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0").transact() diff --git a/tests/ethpm/test_dependencies.py b/tests/ethpm/test_dependencies.py deleted file mode 100644 index b7da59f2f0..0000000000 --- a/tests/ethpm/test_dependencies.py +++ /dev/null @@ -1,69 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.dependencies import ( - Dependencies, -) -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.package import ( - validate_build_dependency, -) - - -@pytest.fixture -def dependencies(dummy_ipfs_backend, piper_coin_manifest, w3): - deps = piper_coin_manifest["buildDependencies"] - dep_pkgs = {dep: Package.from_uri(uri, w3) for dep, uri in deps.items()} - return Dependencies(dep_pkgs) - - -@pytest.fixture -def safe_math_lib_package(safe_math_manifest, w3): - return Package(safe_math_manifest, w3) - - -def test_dependencies_implements_getitem(dependencies, safe_math_lib_package): - assert dependencies["standard-token"].name == "standard-token" - - -def test_dependencies_items(dependencies): - result = dependencies.items() - for key, value in result: - assert key == value.name - assert isinstance(value, Package) - - -def test_dependencies_values(dependencies): - result = dependencies.values() - for value in result: - assert isinstance(value, Package) - - -def test_get_dependency_package(dependencies): - result = dependencies.get_dependency_package("standard-token") - assert isinstance(result, Package) - assert result.name == "standard-token" - - -def test_validate_build_dependencies(dummy_ipfs_backend): - result = validate_build_dependency( - "standard-token", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg" - ) - assert result is None - - -@pytest.mark.parametrize( - "name,uri", - ( - ("standard-token", "invalid_ipfs_uri"), - ("_invalid_pkg_name", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg"), - ("_invalid_pkg_name", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg"), - ), -) -def test_get_build_dependencies_invalidates(name, uri): - with pytest.raises(EthPMValidationError): - validate_build_dependency(name, uri) diff --git a/tests/ethpm/test_deployments.py b/tests/ethpm/test_deployments.py deleted file mode 100644 index 893f7e5344..0000000000 --- a/tests/ethpm/test_deployments.py +++ /dev/null @@ -1,218 +0,0 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ethpm._utils.deployments import ( - get_linked_deployments, - normalize_linked_references, - validate_linked_references, -) -from ethpm.contract import ( - LinkableContract, -) -from ethpm.deployments import ( - Deployments, -) -from ethpm.exceptions import ( - BytecodeLinkingError, - EthPMValidationError, -) -from web3.eth import ( - Contract, -) - -DEPLOYMENT_DATA = { - "SafeMathLib": { - "contractType": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", # noqa: E501 - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } -} - - -@pytest.fixture -def contract_factory(safe_math_lib_package): - return safe_math_lib_package.get_contract_type("SafeMathLib") - - -VALID_CONTRACT_TYPES = {"SafeMathLib": contract_factory} -INVALID_CONTRACT_TYPES = {"INVALID": contract_factory} - - -@pytest.fixture -def deployment(): - return Deployments(DEPLOYMENT_DATA, VALID_CONTRACT_TYPES) - - -@pytest.fixture -def invalid_deployment(): - return Deployments(DEPLOYMENT_DATA, INVALID_CONTRACT_TYPES) - - -def test_deployment_implements_getitem(deployment): - assert deployment["SafeMathLib"] == DEPLOYMENT_DATA["SafeMathLib"] - - -@pytest.mark.parametrize("name", ("", "-abc", "A=bc", "X" * 257)) -def test_deployment_getitem_with_invalid_contract_name_raises_exception( - name, deployment -): - with pytest.raises(EthPMValidationError): - assert deployment[name] - - -def test_deployment_getitem_without_deployment_reference_raises_exception(deployment): - with pytest.raises(KeyError): - deployment["DoesNotExist"] - - -def test_deployment_implements_get_items(deployment): - expected_items = DEPLOYMENT_DATA.items() - assert deployment.items() == expected_items - - -def test_deployment_implements_get_values(deployment): - expected_values = list(DEPLOYMENT_DATA.values()) - assert deployment.values() == expected_values - - -def test_deployment_implements_key_lookup(deployment): - key = "SafeMathLib" in deployment - assert key is True - - -def test_deployment_implements_key_lookup_with_nonexistent_key_raises_exception( - deployment, -): - key = "invalid" in deployment - assert key is False - - -@pytest.mark.parametrize("invalid_name", ("", "-abc", "A=bc", "X" * 257)) -def test_get_instance_with_invalid_name_raises_exception(deployment, invalid_name): - with pytest.raises(EthPMValidationError): - deployment.get_instance(invalid_name) - - -def test_get_instance_without_reference_in_deployments_raises_exception(deployment): - with pytest.raises(KeyError): - deployment.get_instance("InvalidContract") - - -def test_deployments_get_instance(safe_math_lib_package): - deps = safe_math_lib_package.deployments - safe_math_instance = deps.get_instance("SafeMathLib") - assert isinstance(safe_math_instance, Contract) - assert safe_math_instance.bytecode == to_bytes( - hexstr=safe_math_lib_package.manifest["contractTypes"]["SafeMathLib"][ - "deploymentBytecode" - ]["bytecode"] - ) - - -def test_deployments_get_instance_with_contract_alias(safe_math_lib_package_with_alias): - deps = safe_math_lib_package_with_alias.deployments - safe_math_instance = deps.get_instance("safe-math-lib-alias") - assert isinstance(safe_math_instance, Contract) - assert safe_math_instance.bytecode == to_bytes( - hexstr=safe_math_lib_package_with_alias.manifest["contractTypes"][ - "SafeMathLib" - ]["deploymentBytecode"]["bytecode"] - ) - - -def test_deployments_get_instance_with_link_dependency(escrow_package): - deployments = escrow_package.deployments - escrow_deployment = deployments.get_instance("Escrow") - assert isinstance(escrow_deployment, LinkableContract) - assert not escrow_deployment.needs_bytecode_linking - - -def test_get_linked_deployments(escrow_package): - escrow_manifest = escrow_package.manifest - all_deployments = list(escrow_manifest["deployments"].values())[0] - actual_linked_deployments = get_linked_deployments(all_deployments) - assert actual_linked_deployments == {"Escrow": all_deployments["Escrow"]} - # integration via package.deployments - deployments = escrow_package.deployments - assert len(deployments.contract_instances) == 2 - - -@pytest.mark.parametrize( - "deployments", - ( - ( - { - "Escrow": { - "contractType": "Escrow", - "address": "0x8c1968deB27251A3f1F4508df32dA4dfD1b7b57f", - "transaction": "0xc60e32c63abf34579390ef65d83cc5eb52225de38c3eeca2e5afa961d71c16d0", # noqa: E501 - "block": "0x4d1a618802bb87752d95db453dddeea622820424a2f836bedf8769a67ee276b8", # noqa: E501 - "runtimeBytecode": { - "linkDependencies": [ - {"offsets": [200], "type": "reference", "value": "filler"}, - { - "offsets": [301, 495], - "type": "reference", - "value": "Escrow", - }, - ] - }, - } - }, - ) - ), -) -def test_get_linked_deployments_raises_exception_with_self_reference(deployments): - with pytest.raises(BytecodeLinkingError): - get_linked_deployments(deployments) - - -@pytest.mark.parametrize( - "link_data,expected", - ( - ( - [ - {"offsets": [1], "type": "reference", "value": "123"}, - {"offsets": [2, 3], "type": "literal", "value": "abc"}, - ], - ((1, "reference", "123"), (2, "literal", "abc"), (3, "literal", "abc")), - ), - ( - [{"offsets": [1, 2, 3], "type": "literal", "value": "123"}], - ((1, "literal", "123"), (2, "literal", "123"), (3, "literal", "123")), - ), - ), -) -def test_normalize_linked_references(link_data, expected): - link_deps = normalize_linked_references(link_data) - assert link_deps == expected - - -@pytest.mark.parametrize( - "link_deps,bytecode", - ( - (((1, b"abc"),), b"xabc"), - (((1, b"a"), (5, b"xx"), (15, b"1")), b"0a000xx000000001"), - ), -) -def test_validate_linked_references(link_deps, bytecode): - result = validate_linked_references(link_deps, bytecode) - assert result is None - - -@pytest.mark.parametrize( - "link_deps,bytecode", - ( - (((0, b"abc"),), b"xabc"), - (((2, b"abc"),), b"xabc"), - (((8, b"abc"),), b"xabc"), - (((1, b"a"), (5, b"xxx"), (15, b"1")), b"0a000xx000000001"), - ), -) -def test_validate_linked_references_invalidates(link_deps, bytecode): - with pytest.raises(EthPMValidationError): - validate_linked_references(link_deps, bytecode) diff --git a/tests/ethpm/test_get_build_dependencies.py b/tests/ethpm/test_get_build_dependencies.py deleted file mode 100644 index 48d0abffb4..0000000000 --- a/tests/ethpm/test_get_build_dependencies.py +++ /dev/null @@ -1,52 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.dependencies import ( - Dependencies, -) -from ethpm.exceptions import ( - EthPMValidationError, - FailureToFetchIPFSAssetsError, -) - - -@pytest.fixture -def piper_coin_pkg(piper_coin_manifest, w3): - return Package(piper_coin_manifest, w3) - - -def test_get_build_dependencies(dummy_ipfs_backend, piper_coin_pkg, w3): - build_deps = piper_coin_pkg.build_dependencies - assert isinstance(build_deps, Dependencies) - - -def test_get_build_dependencies_with_invalid_uris( - dummy_ipfs_backend, piper_coin_pkg, w3 -): - piper_coin_pkg.manifest["buildDependencies"]["standard-token"] = "invalid_ipfs_uri" - with pytest.raises(FailureToFetchIPFSAssetsError): - piper_coin_pkg.build_dependencies - - -def test_get_build_dependencies_without_dependencies_raises_exception( - piper_coin_manifest, w3 -): - piper_coin_manifest.pop("buildDependencies", None) - pkg = Package(piper_coin_manifest, w3) - with pytest.raises( - EthPMValidationError, match="Manifest doesn't have any build dependencies" - ): - pkg.build_dependencies - - -def test_get_build_dependencies_with_empty_dependencies_raises_exception( - dummy_ipfs_backend, piper_coin_manifest, w3 -): - piper_coin_manifest["buildDependencies"] = {} - pkg = Package(piper_coin_manifest, w3) - with pytest.raises( - EthPMValidationError, match="Manifest's build dependencies key is empty" - ): - pkg.build_dependencies diff --git a/tests/ethpm/test_get_deployments.py b/tests/ethpm/test_get_deployments.py deleted file mode 100644 index f5fe3d417a..0000000000 --- a/tests/ethpm/test_get_deployments.py +++ /dev/null @@ -1,49 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.deployments import ( - Deployments, -) -from ethpm.exceptions import ( - EthPMValidationError, -) - - -def test_get_deployments_with_no_deployments(w3, manifest_with_empty_deployments): - package = Package(manifest_with_empty_deployments, w3) - assert package.deployments == {} - - -def test_get_deployments_with_no_deployments_raises_exception( - w3, manifest_with_no_deployments -): - package = Package(manifest_with_no_deployments, w3) - assert package.deployments == {} - - -def test_get_deployments_with_no_match_raises_exception( - manifest_with_no_matching_deployments, w3 -): - package = Package(manifest_with_no_matching_deployments, w3) - with pytest.raises( - EthPMValidationError, match="Package has no matching URIs on chain." - ): - package.deployments - - -def test_get_deployments_with_multiple_matches_raises_exception( - manifest_with_multiple_matches, w3 -): - package = Package(manifest_with_multiple_matches, w3) - with pytest.raises( - EthPMValidationError, match="Package has too many \\(2\\) matching URIs" - ): - package.deployments - - -def test_get_deployments_with_a_match_returns_deployments(w3, safe_math_lib_package): - deployment = safe_math_lib_package.deployments - assert isinstance(deployment, Deployments) - assert "SafeMathLib" in deployment diff --git a/tests/ethpm/test_package.py b/tests/ethpm/test_package.py deleted file mode 100644 index d16b6ae998..0000000000 --- a/tests/ethpm/test_package.py +++ /dev/null @@ -1,123 +0,0 @@ -import pytest - -from eth_utils import ( - is_same_address, -) - -from ethpm.exceptions import ( - EthPMValidationError, - InsufficientAssetsError, -) -from ethpm.package import ( - Package, -) -from web3 import ( - Web3, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.fixture() -def safe_math_package(get_manifest, w3): - safe_math_manifest = get_manifest("safe-math-lib") - return Package(safe_math_manifest, w3) - - -@pytest.fixture() -def deployed_safe_math(safe_math_package, w3): - SafeMath = safe_math_package.get_contract_factory("SafeMathLib") - tx_hash = SafeMath.constructor().transact() - tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - return safe_math_package, tx_receipt.contractAddress - - -def test_package_object_instantiates_with_a_web3_object(all_manifests, w3): - package = Package(all_manifests, w3) - assert package.w3 is w3 - - -def test_update_web3(deployed_safe_math, w3): - new_w3 = Web3(Web3.EthereumTesterProvider()) - original_package, _ = deployed_safe_math - assert original_package.w3 is w3 - new_package = original_package.update_w3(new_w3) - assert new_package.w3 is new_w3 - assert original_package is not new_package - assert original_package.manifest == new_package.manifest - with pytest.raises( - EthPMValidationError, match="Package has no matching URIs on chain." - ): - new_package.deployments - - -def test_get_contract_factory_with_default_web3(safe_math_package, w3): - contract_factory = safe_math_package.get_contract_factory("SafeMathLib") - assert hasattr(contract_factory, "address") - assert hasattr(contract_factory, "abi") - assert hasattr(contract_factory, "bytecode") - assert hasattr(contract_factory, "bytecode_runtime") - - -def test_get_contract_factory_with_missing_contract_types(safe_math_package, w3): - safe_math_package.manifest.pop("contractTypes", None) - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_factory("SafeMathLib") - - -def test_get_contract_factory_throws_if_name_isnt_present(safe_math_package): - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_factory("DoesNotExist") - - -def test_get_contract_instance(deployed_safe_math): - safe_math_package, address = deployed_safe_math - contract_instance = safe_math_package.get_contract_instance("SafeMathLib", address) - assert contract_instance.abi is not False - assert is_same_address(contract_instance.address, address) - - -def test_get_contract_instance_throws_with_insufficient_assets(deployed_safe_math): - safe_math_package, address = deployed_safe_math - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_instance("IncorrectLib", address) - safe_math_package.manifest["contractTypes"]["SafeMathLib"].pop("abi") - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_instance("SafeMathLib", address) - - -def test_package_object_properties(safe_math_package): - assert safe_math_package.name == "safe-math-lib" - assert safe_math_package.version == "1.0.0" - assert safe_math_package.manifest_version == "ethpm/3" - assert safe_math_package.uri is None - assert safe_math_package.__repr__() == "" - assert safe_math_package.contract_types == ["SafeMathLib"] - - -def test_cached_properties( - piper_coin_manifest, safe_math_lib_package, safe_math_lib_package_with_alias, w3 -): - package1 = Package(piper_coin_manifest, w3) - package2 = Package(piper_coin_manifest, w3) - first_build_dependencies_package1 = package1.build_dependencies.items() - second_build_dependencies_package1 = package1.build_dependencies.items() - assert first_build_dependencies_package1 == second_build_dependencies_package1 - first_build_dependencies_package2 = package2.build_dependencies.items() - second_build_dependencies_package2 = package2.build_dependencies.items() - assert first_build_dependencies_package2 == first_build_dependencies_package2 - assert not first_build_dependencies_package1 == first_build_dependencies_package2 - assert not second_build_dependencies_package1 == second_build_dependencies_package2 - package1 = safe_math_lib_package - first_deployments_package1 = package1.deployments - second_deployments_package1 = package1.deployments - assert first_deployments_package1 == second_deployments_package1 - package2 = safe_math_lib_package_with_alias - first_deployments_package2 = package2.deployments - second_deployments_package2 = package2.deployments - assert first_deployments_package2 == second_deployments_package2 - assert not first_deployments_package1 == first_deployments_package2 - assert not second_deployments_package1 == second_deployments_package2 diff --git a/tests/ethpm/test_package_init.py b/tests/ethpm/test_package_init.py deleted file mode 100644 index ada3389e53..0000000000 --- a/tests/ethpm/test_package_init.py +++ /dev/null @@ -1,143 +0,0 @@ -import json -import os -from pathlib import ( - Path, -) -import pytest - -from ethpm import ( - Package, -) -from ethpm.exceptions import ( - CannotHandleURI, - EthPMValidationError, -) - -# TODO: Add proper authentication to IPFS calls -pytest.skip( - "Need to properly add authorization as of 8/10/2022", allow_module_level=True -) - - -@pytest.fixture -def valid_manifest_from_path(tmpdir): - valid_manifest = '{"manifest":"ethpm/3","name":"foo","version":"1.0.0"}' - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write(valid_manifest) - yield Path(str(temp_manifest)) - - -@pytest.fixture -def invalid_manifest_from_path(tmpdir): - invalid_manifest = '{"manifest":"xx","name":"foo","version":"1.0.0"}' - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write(invalid_manifest) - yield Path(str(temp_manifest)) - - -@pytest.fixture -def non_json_manifest(tmpdir): - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write("This is invalid json") - yield Path(str(temp_manifest)) - - -def test_init_from_minimal_valid_manifest(w3): - minimal_manifest = { - "name": "foo", - "manifest": "ethpm/3", - "version": "1.0.0", - } - - Package(minimal_manifest, w3) - - -def test_init_with_outdated_ethpm_manifest(w3): - v2_manifest = { - "package_name": "foo", - "manifest_version": "2", - "version": "1.0.0", - } - with pytest.raises(EthPMValidationError): - Package(v2_manifest, w3) - - -def test_package_init_for_all_manifest_use_cases(all_manifests, w3): - package = Package(all_manifests, w3) - assert isinstance(package, Package) - - -def test_package_init_for_manifest_with_build_dependency(piper_coin_manifest, w3): - pkg = Package(piper_coin_manifest, w3) - assert isinstance(pkg, Package) - - -def test_init_from_invalid_manifest_data(w3): - with pytest.raises(EthPMValidationError): - Package({}, w3) - - -def test_init_from_invalid_argument_type(w3): - with pytest.raises(TypeError): - Package("not a manifest", w3) - - -def test_from_file_fails_with_missing_filepath(tmpdir, w3): - path = os.path.join(str(tmpdir.mkdir("invalid")), "manifest.json") - - assert not os.path.exists(path) - with pytest.raises(FileNotFoundError): - Package.from_file(Path(path), w3) - - -def test_from_file_fails_with_non_json(non_json_manifest, w3): - with pytest.raises(json.JSONDecodeError): - Package.from_file(non_json_manifest, w3) - - -def test_from_file_fails_with_invalid_manifest(invalid_manifest_from_path, w3): - with pytest.raises(EthPMValidationError): - Package.from_file(invalid_manifest_from_path, w3) - - -def test_from_file_succeeds_with_valid_manifest(valid_manifest_from_path, w3): - assert Package.from_file(valid_manifest_from_path, w3) - - -def test_from_file_raises_type_error_with_invalid_param_type(): - with pytest.raises(TypeError): - Package.from_file(1) - - -# -# From URI -# - -VALID_IPFS_PKG = "ipfs://QmdQfNxmcfGjeVwsXEBLCh5CDYsr2VyZtXoqdVm6F26JJE" - - -def test_package_from_uri_with_valid_uri(w3): - package = Package.from_uri(VALID_IPFS_PKG, w3) - assert package.name == "standard-token" - assert isinstance(package, Package) - - -@pytest.mark.parametrize( - "uri", - ( - # Invalid - "123", - "ipfs://", - "http://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "ipfsQmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - # Unsupported - "erc111://packages.zeppelin.os/owned", - "bzz://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - ), -) -@pytest.mark.skipif( - "WEB3_INFURA_PROJECT_ID" not in os.environ, reason="Infura API key unavailable" -) -def test_package_from_uri_rejects_invalid_ipfs_uri(uri, w3): - with pytest.raises(CannotHandleURI): - Package.from_uri(uri, w3) diff --git a/tests/ethpm/test_uri.py b/tests/ethpm/test_uri.py deleted file mode 100644 index 5d34fd7598..0000000000 --- a/tests/ethpm/test_uri.py +++ /dev/null @@ -1,289 +0,0 @@ -import pytest - -from ethpm.backends.http import ( - is_valid_api_github_uri, -) -from ethpm.backends.registry import ( - parse_registry_uri, -) -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.uri import ( - create_content_addressed_github_uri, - is_valid_content_addressed_github_uri, -) - - -@pytest.mark.parametrize( - "uri,expected", - ( - ({}, False), - (123, False), - ("xxx", False), - # invalid scheme - ("api.github.com/repos/contents/path", False), - ("http://api.github.com/repos/contents/path", False), - # invalid authority - ("http://raw.githubusercontent.com/repos/contents/path", False), - ("https://github.com/repos/contents/path", False), - # invalid path - ("https://api.github.com", False), - ("https://api.github.com/", False), - ("https://api.github.com/contents/", False), - ("https://api.github.com/repos/", False), - # valid github urls - ("https://api.github.com/repos/contents/path", True), - ( - "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/contracts/Owned.sol", # noqa: E501 - True, - ), - ), -) -def test_is_valid_github_uri(uri, expected): - actual = is_valid_api_github_uri(uri) - assert actual is expected - - -@pytest.mark.parametrize( - "uri,expected", - ( - ( - "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/contracts/Owned.sol", # noqa: E501 - False, - ), - ( - "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480", # noqa: E501 - True, - ), - ), -) -def test_is_valid_content_addressed_github_uri(uri, expected): - actual = is_valid_content_addressed_github_uri(uri) - assert actual is expected - - -def test_create_github_uri(): - api_uri = "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/1.0.0.json" # noqa: E501 - expected_blob_uri = "https://api.github.com/repos/ethpm/ethpm-spec/git/blobs/8f9dc767d4c8b31fec4a08d9c0858d4f37b83180" # noqa: E501 - actual_blob_uri = create_content_addressed_github_uri(api_uri) - assert actual_blob_uri == expected_blob_uri - - -@pytest.mark.parametrize( - "uri,expected", - ( - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - ["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", None, None, None, None], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:5/owned", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "5", - "owned", - None, - None, - None, - ], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned@1.0.0", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "owned", - "1.0.0", - None, - None, - ], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet@2.8.0/", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - None, - None, - ], - ), - # ethpm scheme - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet@2.8.0", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - None, - None, - ], - ), - # w/o chain_id - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/owned", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "owned", - None, - None, - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@2.8.0", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - None, - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@8%400", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "8@0", - None, - None, - ], - ), - # escaped chars - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet@8%400", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "8@0", - None, - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet@%250", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "%0", - None, - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet@8%400/", - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "8@0", - None, - None, - ], - ), - # with namespaced manifest contents - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@2.8.0/deployments", # noqa: E501 - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - "deployments", - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@2.8.0/deployments/", # noqa: E501 - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - "deployments", - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@2.8.0/deployments/WalletContract", # noqa: E501 - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - "deployments/WalletContract", - None, - ], - ), - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@2.8.0/deployments/WalletContract/", # noqa: E501 - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "2.8.0", - "deployments/WalletContract", - None, - ], - ), - # unescaped chars & namespaced assets - ( - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@20%26/deployments/WalletContract/", # noqa: E501 - [ - "0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", - "1", - "wallet", - "20&", - "deployments/WalletContract", - None, - ], - ), - ), -) -def test_parse_registry_uri(uri, expected): - ( - address, - chain_id, - pkg_name, - pkg_version, - namespaced_asset, - ens, - ) = parse_registry_uri(uri) - assert address == expected[0] - assert chain_id == expected[1] - assert pkg_name == expected[2] - assert pkg_version == expected[3] - assert namespaced_asset == expected[4] - - -@pytest.mark.parametrize( - "uri", - ( - # invalid scheme - "ethpx://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned@1.0.0", - "erc1318://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned@1.0.0", - "erc1318://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned@1.0.0/", - # missing package_name - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/@1.0.0", - # unescaped chars in package_name - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/a!bc@1.0.0", - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/ab@@1.0.0", - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/!bc@1.0.0", - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/!bc@1.0.0/", - # namespaced asset and missing version - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet/deployments/WalletContract", # noqa: E501 - "ethpm://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet@/deployments/WalletContract", # noqa: E501 - ), -) -def test_invalid_registry_uris(uri): - with pytest.raises(EthPMValidationError): - parse_registry_uri(uri) diff --git a/tests/ethpm/tools/test_builder.py b/tests/ethpm/tools/test_builder.py deleted file mode 100644 index d05f060e2c..0000000000 --- a/tests/ethpm/tools/test_builder.py +++ /dev/null @@ -1,835 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest - -from eth_utils import ( - to_canonical_address, -) -from eth_utils.toolz import ( - assoc, - assoc_in, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.backends.ipfs import ( - get_ipfs_backend, -) -from ethpm.exceptions import ( - EthPMValidationError, - ManifestBuildingError, -) -from ethpm.tools import ( - get_ethpm_local_manifest, - get_ethpm_spec_manifest, -) -from ethpm.tools.builder import ( - as_package, - authors, - build, - build_dependency, - contract_type, - deployment, - deployment_type, - description, - init_manifest, - inline_source, - keywords, - license, - links, - manifest_version, - normalize_contract_type, - package_name, - pin_source, - source_inliner, - source_pinner, - validate, - version, - write_to_disk, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - link, - linker, -) - -BASE_MANIFEST = {"name": "package", "manifest": "ethpm/3", "version": "1.0.0"} - - -@pytest.fixture -def owned_package(ethpm_spec_dir): - manifest = get_ethpm_spec_manifest("owned", "v3.json") - # source_id missing `./` prefix in ethpm-spec - # ("Owned.sol"/"./Owned.sol" though both are valid) - source_obj = manifest["sources"].pop("Owned.sol") - updated_manifest = assoc_in(manifest, ["sources", "./Owned.sol"], source_obj) - - compiler = get_ethpm_local_manifest("owned", "output_v3.json")["contracts"] - contracts_dir = ethpm_spec_dir / "examples" / "owned" / "contracts" - return contracts_dir, updated_manifest, compiler - - -# todo validate no duplicate contracts in package - - -@pytest.fixture -def standard_token_package(ethpm_spec_dir): - standard_token_dir = ethpm_spec_dir / "examples" / "standard-token" - manifest = get_ethpm_spec_manifest("standard-token", "v3.json") - compiler = get_ethpm_local_manifest("standard-token", "output_v3.json")["contracts"] - contracts_dir = standard_token_dir / "contracts" - return contracts_dir, manifest, compiler - - -@pytest.fixture -def registry_package(): - root = ASSETS_DIR / "registry" - compiler = json.loads(Path(root / "solc_output.json").read_text())["contracts"] - contracts_dir = root / "contracts" - manifest = json.loads((root / "v3.json").read_text()) - return contracts_dir, manifest, compiler - - -@pytest.fixture -def manifest_dir(tmpdir): - return Path(tmpdir.mkdir("sub")) - - -def test_builder_simple_with_package(w3): - package = build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - validate(), - as_package(w3), - ) - assert isinstance(package, Package) - assert package.version == "1.0.0" - - -PRETTY_MANIFEST = """{ - "manifest": "ethpm/3", - "name": "package", - "version": "1.0.0" -}""" - -MINIFIED_MANIFEST = '{"manifest":"ethpm/3","name":"package","version":"1.0.0"}' - -OWNED_CONTRACT = "// SPDX-License-Identifier: MIT\npragma solidity ^0.6.8;\n\ncontract Owned {\n address owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }\n\n constructor() public {\n owner = msg.sender;\n }\n}" # noqa: E501 - - -def test_builder_writes_manifest_to_disk(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - validate(), - write_to_disk( - manifest_root_dir=manifest_dir, manifest_name="1.0.0.json", prettify=True - ), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == PRETTY_MANIFEST - - -def test_builder_to_disk_uses_default_cwd(manifest_dir, monkeypatch): - monkeypatch.chdir(manifest_dir) - build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - write_to_disk(), - validate(), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -def test_to_disk_writes_minified_manifest_as_default(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - write_to_disk(manifest_root_dir=manifest_dir, manifest_name="1.0.0.json"), - validate(), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -def test_to_disk_uses_default_manifest_name(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - write_to_disk(manifest_root_dir=manifest_dir), - validate(), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -@pytest.mark.parametrize( - "write_to_disk_fn", - ( - write_to_disk(manifest_root_dir=Path("not/a/directory")), - write_to_disk(manifest_name="invalid_name"), - ), -) -def test_to_disk_with_invalid_args_raises_exception(manifest_dir, write_to_disk_fn): - with pytest.raises(ManifestBuildingError): - build( - {}, - package_name("package"), - manifest_version("ethpm/3"), - version("1.0.0"), - write_to_disk_fn, - ) - - -def test_builder_with_manifest_validation(): - with pytest.raises(EthPMValidationError, match="_invalid_package_name"): - build( - {}, - package_name("_invalid_package_name"), - manifest_version("ethpm/3"), - version("1.0.0"), - validate(), - ) - - -@pytest.mark.parametrize( - "fn,value", - ( - (authors("some", "guy"), {"authors": ["some", "guy"]}), - (license("MIT"), {"license": "MIT"}), - (description("This is a package."), {"description": "This is a package."}), - (keywords("awesome", "package"), {"keywords": ["awesome", "package"]}), - ( - links(documentation="ipfs..", website="www"), - {"links": {"documentation": "ipfs..", "website": "www"}}, - ), - ), -) -def test_builder_with_simple_meta_fields(fn, value): - manifest = build(BASE_MANIFEST, fn, validate()) - expected = assoc(BASE_MANIFEST, "meta", value) - assert manifest == expected - - -def test_builder_simple_with_multi_meta_field(): - manifest = build( - BASE_MANIFEST, - authors("some", "guy"), - license("MIT"), - description("description"), - keywords("awesome", "package"), - links(website="www", repository="github"), - validate(), - ) - expected = assoc( - BASE_MANIFEST, - "meta", - { - "license": "MIT", - "authors": ["some", "guy"], - "description": "description", - "keywords": ["awesome", "package"], - "links": {"website": "www", "repository": "github"}, - }, - ) - assert manifest == expected - - -def test_builder_with_inline_source(owned_package, monkeypatch): - root, _, compiler_output = owned_package - - monkeypatch.chdir(root) - manifest = build(BASE_MANIFEST, inline_source("Owned", compiler_output), validate()) - - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": { - "content": OWNED_CONTRACT, - "installPath": "./Owned.sol", - "type": "solidity", - } - }, - ) - assert manifest == expected - - -def test_builder_with_source_inliner(owned_package, monkeypatch): - root, _, compiler_output = owned_package - - monkeypatch.chdir(root) - inliner = source_inliner(compiler_output) - manifest = build(BASE_MANIFEST, inliner("Owned"), validate()) - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": { - "content": OWNED_CONTRACT, - "installPath": "./Owned.sol", - "type": "solidity", - } - }, - ) - assert manifest == expected - - -def test_builder_with_inline_source_with_package_root_dir_arg(owned_package): - root, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - inline_source("Owned", compiler_output, package_root_dir=root), - validate(), - ) - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": { - "content": OWNED_CONTRACT, - "installPath": "./Owned.sol", - "type": "solidity", - } - }, - ) - print(manifest) - print("-") - print(expected) - assert manifest == expected - - -def test_builder_with_pin_source(owned_package, dummy_ipfs_backend): - root, expected, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - - manifest = build( - {}, - package_name("owned"), - manifest_version("ethpm/3"), - version("1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." # noqa: E501 - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pin_source("Owned", compiler_output, ipfs_backend, root), - validate(), - ) - - assert manifest == expected - - -def test_builder_with_pinner(owned_package, dummy_ipfs_backend): - root, expected, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - pinner = source_pinner(compiler_output, ipfs_backend, root) - manifest = build( - {}, - package_name("owned"), - manifest_version("ethpm/3"), - version("1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." # noqa: E501 - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pinner("Owned"), - validate(), - ) - - assert manifest == expected - - -def test_builder_with_init_manifest(owned_package, dummy_ipfs_backend): - root, expected, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - - manifest = build( - init_manifest(package_name="owned", version="1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." # noqa: E501 - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pin_source("Owned", compiler_output, ipfs_backend, root), - validate(), - ) - - assert manifest == expected - - -def test_builder_with_default_contract_types(owned_package): - _, _, compiler_output = owned_package - - manifest = build(BASE_MANIFEST, contract_type("Owned", compiler_output), validate()) - - contract_type_data = normalize_contract_type( - compiler_output["Owned.sol"]["Owned"], "Owned.sol" - ) - compilers_data = contract_type_data.pop("compiler") - compilers_data["contractTypes"] = ["Owned"] - expected_with_contract_type = assoc( - BASE_MANIFEST, "contractTypes", {"Owned": contract_type_data} - ) - expected = assoc(expected_with_contract_type, "compilers", [compilers_data]) - assert manifest == expected - - -def test_builder_with_single_alias_kwarg(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - contract_type("Owned", compiler_output, alias="OwnedAlias"), - validate(), - ) - - contract_type_data = normalize_contract_type( - compiler_output["Owned.sol"]["Owned"], "Owned.sol" - ) - compilers_data = contract_type_data.pop("compiler") - compilers_data["contractTypes"] = ["OwnedAlias"] - expected_with_contract_type = assoc( - BASE_MANIFEST, - "contractTypes", - {"OwnedAlias": assoc(contract_type_data, "contractType", "Owned")}, - ) - expected = assoc(expected_with_contract_type, "compilers", [compilers_data]) - assert manifest == expected - - -def test_builder_without_alias_and_with_select_contract_types(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - contract_type("Owned", compiler_output, abi=True, source_id=True), - validate(), - ) - - contract_type_data = normalize_contract_type( - compiler_output["Owned.sol"]["Owned"], "Owned.sol" - ) - omitted_fields = ("deploymentBytecode", "userdoc", "devdoc", "compiler") - selected_data = { - k: v for k, v in contract_type_data.items() if k not in omitted_fields - } - expected = assoc(BASE_MANIFEST, "contractTypes", {"Owned": selected_data}) - assert manifest == expected - - -def test_builder_with_alias_and_select_contract_types(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - contract_type( - "Owned", - compiler_output, - alias="OwnedAlias", - abi=True, - compiler=False, - devdoc=True, - userdoc=True, - deployment_bytecode=True, - runtime_bytecode=False, - source_id=True, - ), - validate(), - ) - - contract_type_data = normalize_contract_type( - compiler_output["Owned.sol"]["Owned"], "Owned.sol" - ) - contract_type_data.pop("compiler") - expected = assoc( - BASE_MANIFEST, - "contractTypes", - {"OwnedAlias": assoc(contract_type_data, "contractType", "Owned")}, - ) - assert manifest == expected - - -def test_builder_manages_duplicate_compilers(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - contract_type( - "Owned", - compiler_output, - abi=True, - compiler=True, - source_id=True, - ), - contract_type( - "Owned", - compiler_output, - alias="OwnedAlias", - abi=True, - compiler=True, - source_id=True, - ), - validate(), - ) - contract_type_data = normalize_contract_type( - compiler_output["Owned.sol"]["Owned"], "Owned.sol" - ) - compiler_data = contract_type_data.pop("compiler") - contract_type_data.pop("deploymentBytecode") - contract_type_data.pop("devdoc") - contract_type_data.pop("userdoc") - compiler_data_with_contract_types = assoc( - compiler_data, "contractTypes", ["Owned", "OwnedAlias"] - ) - expected_with_contract_types = assoc( - BASE_MANIFEST, - "contractTypes", - { - "Owned": assoc(contract_type_data, "contractType", "Owned"), - "OwnedAlias": assoc(contract_type_data, "contractType", "Owned"), - }, - ) - expected_with_contract_types["contractTypes"]["Owned"].pop("contractType") - expected = assoc( - expected_with_contract_types, "compilers", [compiler_data_with_contract_types] - ) - assert manifest == expected - - -def test_builder_raises_exception_if_selected_contract_type_missing_from_solc( - owned_package, -): - _, _, compiler_output = owned_package - with pytest.raises(ManifestBuildingError, match="runtimeBytecode not available"): - build( - BASE_MANIFEST, - contract_type("Owned", compiler_output, abi=True, runtime_bytecode=True), - ) - - -def test_builder_with_standard_token_manifest( - standard_token_package, dummy_ipfs_backend, monkeypatch -): - root, expected_manifest, compiler_output = standard_token_package - ipfs_backend = get_ipfs_backend() - - monkeypatch.chdir(root) - manifest = build( - {}, - package_name("standard-token"), - manifest_version("ethpm/3"), - version("1.0.0"), - pin_source("StandardToken", compiler_output, ipfs_backend), - pin_source("Token", compiler_output, ipfs_backend), - contract_type( - "StandardToken", compiler_output, abi=True, devdoc=True, source_id=True - ), - contract_type( - "Token", - compiler_output, - abi=True, - devdoc=True, - userdoc=True, - source_id=True, - ), - validate(), - ) - assert manifest == expected_manifest - - -def test_builder_with_link_references( - registry_package, dummy_ipfs_backend, monkeypatch -): - root, expected_manifest, compiler_output = registry_package - monkeypatch.chdir(root) - inliner = source_inliner(compiler_output) - manifest = build( - {}, - package_name("solidity-registry"), - manifest_version("ethpm/3"), - version("2.0.0"), - inliner("Authorized"), - inliner("IndexedOrderedSetLib"), - inliner("PackageDB"), - inliner("PackageRegistry"), - inliner("PackageRegistryInterface"), - inliner("ReleaseDB"), - inliner("ReleaseValidator"), - contract_type( - "AuthorityInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "Authorized", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "AuthorizedInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "WhitelistAuthority", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "WhitelistAuthorityInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "IndexedOrderedSetLib", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "PackageDB", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "PackageRegistry", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "PackageRegistryInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "ReleaseDB", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - contract_type( - "ReleaseValidator", - compiler_output, - abi=True, - compiler=True, - deployment_bytecode=True, - runtime_bytecode=True, - devdoc=True, - source_id=True, - ), - validate(), - ) - assert manifest == expected_manifest - - -def test_builder_deployment_simple(w3): - expected = json.dumps( - { - "name": "package", - "version": "1.0.0", - "manifest": "ethpm/3", - "deployments": { - "blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef": { # noqa: E501 - "Owned": { - "contractType": "Owned", - "address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601", - } - } - }, - } - ) - manifest = build( - BASE_MANIFEST, - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="Owned", - contract_type="Owned", - address=to_canonical_address("0xd3cda913deb6f67967b99d67acdfa1712c293601"), - ), - validate(), - ) - assert manifest == json.loads(expected) - - -@pytest.fixture -def escrow_package(w3, deployer, ethpm_spec_dir): - manifest = ethpm_spec_dir / "examples" / "escrow" / "v3.json" - escrow_deployer = deployer(manifest) - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0]), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - escrow_package = escrow_deployer.deploy("Escrow") - return escrow_package, w3 - - -def test_builder_deployment_type_complex(escrow_package): - escrow, w3 = escrow_package - escrow_dep_type = deployment_type( - contract_instance="Escrow", - contract_type="Escrow", - deployment_bytecode={ - "bytecode": "0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001" # noqa: E501 - }, - runtime_bytecode={ - "bytecode": "0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029" # noqa: E501 - }, - compiler={ - "name": "solc", - "version": "0.4.24+commit.e67f0147.Emscripten.clang", - "settings": {"optimize": True}, - }, - ) - safesendlib_dep_type = deployment_type( - contract_instance="SafeSendLib", contract_type="SafeSendLib" - ) - manifest = build( - {}, - package_name("escrow"), - version("1.0.0"), - manifest_version("ethpm/3"), - escrow_dep_type( - block_uri="blockchain://1111111111111111111111111111111111111111111111111111111111111111/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - address=escrow.deployments.get_instance("Escrow").address, - ), - # dep_type with block uri - safesendlib_dep_type( - block_uri="blockchain://1111111111111111111111111111111111111111111111111111111111111111/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - address=escrow.deployments.get_instance("SafeSendLib").address, - ), - # simple deployment - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="Escrow", - contract_type="Escrow", - address=escrow.deployments.get_instance("Escrow").address, - ), - # simple deployment - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="SafeSendLib", - contract_type="SafeSendLib", - address=escrow.deployments.get_instance("SafeSendLib").address, - ), - validate(), - ) - assert len(manifest["deployments"].keys()) == 2 - assert len(list(manifest["deployments"].values())[0]) == 2 - assert len(list(manifest["deployments"].values())[1]) == 2 - - -def test_builder_with_single_build_dependency(): - expected_build_dep = { - "package": "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - } - expected = assoc_in(BASE_MANIFEST, ["buildDependencies"], expected_build_dep) - actual = build( - BASE_MANIFEST, - build_dependency( - "package", "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - ), - validate(), - ) - assert actual == expected - - -def test_builder_with_multiple_build_dependencies(): - expected_build_deps = { - "escrow": "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv", - "package": "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW", - } - expected = assoc_in(BASE_MANIFEST, ["buildDependencies"], expected_build_deps) - actual = build( - BASE_MANIFEST, - build_dependency( - "package", "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - ), - build_dependency( - "escrow", "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" - ), - validate(), - ) - assert actual == expected - - -def test_builder_with_invalid_uri(): - with pytest.raises( - EthPMValidationError, match="is not a supported content-addressed URI" - ): - build( - {}, - package_name("package"), - version("1.0.0"), - manifest_version("ethpm/3"), - build_dependency("package", "www.google.com"), - ) diff --git a/tests/ethpm/tools/test_checker.py b/tests/ethpm/tools/test_checker.py deleted file mode 100644 index 11d5183032..0000000000 --- a/tests/ethpm/tools/test_checker.py +++ /dev/null @@ -1,151 +0,0 @@ -import pytest - -from ethpm.tools.checker import ( - WARNINGS, - check_contract_types, - check_manifest, - check_meta, - check_sources, -) - - -def test_checker_simple(): - warnings = check_manifest({}) - assert warnings == { - "manifest": "Manifest missing a required 'manifest' field.", - "name": "Manifest missing a suggested 'name' field", - "version": "Manifest missing a suggested 'version' field.", - "meta": "Manifest missing a suggested 'meta' field.", - "sources": """Manifest is missing a sources field, """ - """which defines a source tree that should comprise the full source tree """ - """necessary to recompile the contracts contained in this release.""", - "contractTypes": """Manifest does not contain any 'contractTypes'. """ - """Packages should only include contract types that can be found in the """ - """source files for this package. Packages should not include contract types """ - """from dependencies. Packages should not include abstract contracts in the """ - """contract types section of a release.""", - "compilers": "Manifest is missing a suggested `compilers` field.", - } - - -BASIC_MANIFEST = { - "name": "package", - "version": "1.0.0", - "manifest": "ethpm/3", -} - - -@pytest.mark.parametrize( - "manifest,expected", - ( - ({}, {"meta": WARNINGS["meta_missing"]}), - ({"meta": {}}, {"meta": WARNINGS["meta_missing"]}), - ( - {"meta": {"x": "x"}}, - { - "meta.authors": WARNINGS["authors_missing"], - "meta.description": WARNINGS["description_missing"], - "meta.links": WARNINGS["links_missing"], - "meta.keywords": WARNINGS["keywords_missing"], - "meta.license": WARNINGS["license_missing"], - }, - ), - ), -) -def test_check_meta(manifest, expected): - warnings = check_meta(manifest, {}) - assert warnings == expected - - -@pytest.mark.parametrize( - "manifest,expected", - ( - # Sad paths - ({}, {"sources": WARNINGS["sources_missing"]}), - ({"sources": []}, {"sources": WARNINGS["sources_missing"]}), - # Happy path - ({"sources": {"links": "www.github.com"}}, {}), - ), -) -def test_check_sources(manifest, expected): - warnings = check_sources(manifest, {}) - assert warnings == expected - - -@pytest.mark.parametrize( - "manifest,expected", - ( - ({}, {"contractTypes": WARNINGS["contract_type_missing"]}), - ({"contractTypes": {}}, {"contractTypes": WARNINGS["contract_type_missing"]}), - ( - {"contractTypes": {"x": {"runtimeBytecode": {"invalid": "invalid"}}}}, - { - "contractTypes": { - "x": { - "abi": WARNINGS["abi_missing"].format("x"), - "contractType": WARNINGS[ - "contract_type_subfield_missing" - ].format("x"), - "deploymentBytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("x"), - "runtimeBytecode": WARNINGS["bytecode_subfield_missing"].format( - "x", "runtime" - ), - "devdoc": WARNINGS["devdoc_missing"].format("x"), - "userdoc": WARNINGS["userdoc_missing"].format("x"), - } - } - }, - ), - ( - { - "contractTypes": { - "x": { - "deploymentBytecode": [], - "runtimeBytecode": {"bytecode": []}, - }, - "y": { - "abi": [1], - "deploymentBytecode": [], - "runtimeBytecode": [], - }, - } - }, - { - "contractTypes": { - "x": { - "abi": WARNINGS["abi_missing"].format("x"), - "contractType": WARNINGS[ - "contract_type_subfield_missing" - ].format("x"), - "deploymentBytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("x"), - "runtimeBytecode": WARNINGS["bytecode_subfield_missing"].format( - "x", "runtime" - ), - "devdoc": WARNINGS["devdoc_missing"].format("x"), - "userdoc": WARNINGS["userdoc_missing"].format("x"), - }, - "y": { - "contractType": WARNINGS[ - "contract_type_subfield_missing" - ].format("y"), - "deploymentBytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("y"), - "runtimeBytecode": WARNINGS["runtime_bytecode_missing"].format( - "y" - ), - "devdoc": WARNINGS["devdoc_missing"].format("y"), - "userdoc": WARNINGS["userdoc_missing"].format("y"), - }, - } - }, - ), - ), -) -def test_check_contract_types(manifest, expected): - warnings = check_contract_types(manifest, {}) - assert warnings == expected diff --git a/tests/ethpm/validation/test_manifest.py b/tests/ethpm/validation/test_manifest.py deleted file mode 100644 index 33b80e3a6d..0000000000 --- a/tests/ethpm/validation/test_manifest.py +++ /dev/null @@ -1,235 +0,0 @@ -import pytest - -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.manifest import ( - extract_contract_types_from_deployments, - validate_manifest_against_schema, - validate_manifest_deployments, - validate_meta_object, - validate_raw_manifest_format, -) -from ethpm.validation.package import ( - validate_manifest_version, - validate_package_name, -) - - -def test_validate_raw_manifest_configuration_validates_strict_manifests( - all_strict_manifests, -): - assert validate_raw_manifest_format(all_strict_manifests) is None - - -def test_validate_raw_manifest_format_invalidates_pretty_manifests( - all_pretty_manifests, -): - with pytest.raises(EthPMValidationError): - validate_raw_manifest_format(all_pretty_manifests) - - -@pytest.mark.parametrize( - "manifest", - ( - # not alphabetical - '{"x":"y","a":"b"}', - # not UTF-8 - '{"\x80":"b","c":"d"}', - # newlines - '{"a":"b",\n"c":"d"}', - '{"a":"b","c":"d"}\n', - # whitespace - '{"a":"b","c": "d"}', - ), -) -def test_validate_raw_manifest_format_invalidates_invalid_manifests(tmpdir, manifest): - p = tmpdir.mkdir("invalid").join("manifest.json") - p.write(manifest) - invalid_manifest = p.read() - with pytest.raises(EthPMValidationError): - validate_raw_manifest_format(invalid_manifest) - - -def test_validate_manifest_against_all_manifest_types(all_manifests): - assert validate_manifest_against_schema(all_manifests) is None - - -def test_validate_manifest_invalidates(invalid_manifest): - with pytest.raises(EthPMValidationError, match="Manifest invalid for schema"): - validate_manifest_against_schema(invalid_manifest) - - -def test_validate_manifest_deployments_catches_missing_contract_type_references( - manifest_with_conflicting_deployments, -): - with pytest.raises( - EthPMValidationError, match="Manifest missing references to contracts" - ): - validate_manifest_deployments(manifest_with_conflicting_deployments) - - -def test_validate_deployments_for_single_deployment(safe_math_lib_package): - assert validate_manifest_deployments(safe_math_lib_package.manifest) is None - - -def test_validate_deployments_without_deployment(manifest_with_no_deployments): - assert validate_manifest_deployments(manifest_with_no_deployments) is None - - -@pytest.mark.parametrize( - "data,expected", - ( - ([], set()), - ([{"some": {"contractType": "one"}}], {"one"}), - ( - [{"some": {"contractType": "one"}, "other": {"contractType": "two"}}], - {"one", "two"}, - ), - ), -) -def test_extract_contract_types_from_deployments(data, expected): - actual = extract_contract_types_from_deployments(data) - assert actual == expected - - -def test_validate_manifest_version_validates_version_three_string(): - assert validate_manifest_version("ethpm/3") is None - - -@pytest.mark.parametrize("version", (2, 3, "2", "3", b"3")) -def test_validate_manifest_version_invalidates_incorrect_versions(version): - with pytest.raises(EthPMValidationError): - validate_manifest_version(version) - - -@pytest.mark.parametrize( - "meta,extra_fields", - ( - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "A Package that does things.", - "keywords": ["ethpm", "package"], - "links": {"documentation": "ipfs://Qm..."}, - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "A Package that does things.", - "keywords": ["ethpm", "package"], - "links": {"documentation": "ipfs://Qm..."}, - "x-hash": "0x...", - }, - True, - ), - ), -) -def test_validate_meta_object_validates(meta, extra_fields): - result = validate_meta_object(meta, allow_extra_meta_fields=extra_fields) - assert result is None - - -@pytest.mark.parametrize( - "meta,extra_fields", - ( - # With allow_extra_meta_fields=False - ({"invalid": "field"}, False), - ({"license": 123}, False), - ({"license": "MIT", "authors": "auther@gmail.com"}, False), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": ["description", "of", "package"], - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": "singlekw", - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": ["ipfs://Qm"], - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": {"documentation": "ipfs://Qm"}, - "extra": "field", - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": {"documentation": "ipfs://Qm"}, - "x-hash": "0x", - }, - False, - ), - # With allow_extra_meta_fields=True - # Improperly formatted "x" field - ({"license": "MIT", "extra": "field"}, True), - ), -) -def test_validate_meta_object_invalidates(meta, extra_fields): - with pytest.raises(EthPMValidationError): - validate_meta_object(meta, allow_extra_meta_fields=extra_fields) - - -@pytest.mark.parametrize( - "package_name", - ( - "valid", - "Valid", - "pkg1", - "pkg_1", - "pkg-1", - "wallet0", - "wallet_", - "wallet-", - "x" * 256, - ), -) -def test_validate_package_name_with_valid_package_names(package_name): - assert validate_package_name(package_name) is None - - -@pytest.mark.parametrize( - "package_name", - ( - "", - "0", - "_invalid", - "-invalid", - ".invalid", - "wallet.bad", - "x" * 257, - ), -) -def test_validate_package_name_raises_exception_for_invalid_names(package_name): - with pytest.raises(EthPMValidationError): - validate_package_name(package_name) diff --git a/tests/ethpm/validation/test_manifest_assets_are_valid.py b/tests/ethpm/validation/test_manifest_assets_are_valid.py deleted file mode 100644 index 404ea1e62d..0000000000 --- a/tests/ethpm/validation/test_manifest_assets_are_valid.py +++ /dev/null @@ -1,37 +0,0 @@ -import json -import pytest - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.exceptions import ( - InsufficientAssetsError, -) -from ethpm.validation.manifest import ( - validate_manifest_against_schema, -) - -SOURCES_GLOB = "**/*.json" - - -def get_all_manifest_paths(): - # Expects all json in ethpm/assets to be either compiler_output or a manifest - all_use_case_json = set(ASSETS_DIR.glob(SOURCES_GLOB)) - set( - (ASSETS_DIR / "spec").glob(SOURCES_GLOB) - ) - all_manifests = [json for json in all_use_case_json if json.name == "v3.json"] - if not all_manifests: - raise InsufficientAssetsError( - "Error importing manifests for validation, " - "no v3 manifests found in `ethpm/ethpm-spec` submodule" - ) - return all_manifests - - -@pytest.fixture(params=get_all_manifest_paths()) -def manifest(request): - return json.loads(request.param.read_text()) - - -def test_manifest_assets_are_valid(manifest): - assert validate_manifest_against_schema(manifest) is None diff --git a/tests/ethpm/validation/test_misc.py b/tests/ethpm/validation/test_misc.py deleted file mode 100644 index a14b26c27d..0000000000 --- a/tests/ethpm/validation/test_misc.py +++ /dev/null @@ -1,62 +0,0 @@ -import pytest - -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.misc import ( - validate_empty_bytes, - validate_escaped_string, -) - - -@pytest.mark.parametrize( - "offset,length,bytecode", - ( - (0, 3, b"\00\00\00"), - (1, 20, b"\01" + bytearray(20) + b"\01"), - (26, 20, b"\01" + bytearray(20) + b"\01" * 5 + bytearray(20) + b"\01"), - ), -) -def test_validate_empty_bytes(offset, length, bytecode): - result = validate_empty_bytes(offset, length, bytecode) - assert result is None - - -@pytest.mark.parametrize( - "offset,length,bytecode", - ( - (0, 2, b"\00"), - (0, 3, b"\01\01\01"), - (1, 1, b"\00\01\00\01"), - (1, 20, bytearray(20) + b"\01"), - ), -) -def test_validate_empty_bytes_invalidates(offset, length, bytecode): - with pytest.raises(EthPMValidationError): - validate_empty_bytes(offset, length, bytecode) - - -@pytest.mark.parametrize( - "string", - ( - "abcd", - "abcd%40", - "%20%24%26", - ), -) -def test_validate_escaped_strings(string): - validate_escaped_string(string) - - -@pytest.mark.parametrize( - "string", - ( - "@bcd", - "@bcd%40", - "!bcd%40", - "&bcd%40", - ), -) -def test_validate_escaped_strings_invalidates(string): - with pytest.raises(EthPMValidationError): - validate_escaped_string(string) diff --git a/tox.ini b/tox.ini index 774912ae2c..58355431df 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist= py{38,39,310,311}-ens - py{38,39,310,311}-ethpm py{38,39,310,311}-core py{38,39,310,311}-integration-{goethereum,ethtester} py{38,39,310,311}-lint @@ -24,7 +23,6 @@ commands= core_async: pytest {posargs:tests/core -m asyncio} ens: pytest {posargs:tests/ens --ignore=tests/ens/normalization/test_normalize_name_ensip15.py} ensip15: pytest {posargs:tests/ens/normalization/test_normalize_name_ensip15.py -q} - ethpm: pytest {posargs:tests/ethpm} 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} integration-goethereum-ipc_flaky: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ipc.py --flaky} @@ -57,9 +55,9 @@ basepython = [common-lint] extras=linter commands= - flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/ethpm {toxinidir}/tests --exclude {toxinidir}/ethpm/ethpm-spec,{toxinidir}/**/*_pb2.py - black {toxinidir}/ens {toxinidir}/ethpm {toxinidir}/web3 {toxinidir}/tests {toxinidir}/setup.py --exclude /ethpm/ethpm-spec/|/ethpm/_utils/protobuf/ipfs_file_pb2\.py --check - isort --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/ethpm/ {toxinidir}/tests/ + flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/tests + black {toxinidir}/ens {toxinidir}/web3 {toxinidir}/tests {toxinidir}/setup.py --check + isort --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/tests/ mypy -p web3 -p ens --config-file {toxinidir}/mypy.ini [testenv:lint] diff --git a/web3/main.py b/web3/main.py index fe06b8ba93..1f0e7376ee 100644 --- a/web3/main.py +++ b/web3/main.py @@ -1,5 +1,4 @@ import decimal -import warnings from types import ( TracebackType, ) @@ -142,7 +141,6 @@ ) if TYPE_CHECKING: - from web3.pm import PM # noqa: F401 from web3._utils.empty import Empty # noqa: F401 @@ -338,30 +336,6 @@ def attach_modules( def is_encodable(self, _type: TypeStr, value: Any) -> bool: return self.codec.is_encodable(_type, value) - @property - def pm(self) -> "PM": - if hasattr(self, "_pm"): - # ignored b/c property is dynamically set - # via enable_unstable_package_management_api - return self._pm - else: - raise AttributeError( - "The Package Management feature is disabled by default until " - "its API stabilizes. To use these features, please enable them by " - "running `w3.enable_unstable_package_management_api()` and try again." - ) - - def enable_unstable_package_management_api(self) -> None: - if not hasattr(self, "_pm"): - warnings.warn( - "The ``ethPM`` module is no longer being maintained and will be " - "deprecated with ``web3.py`` version 7", - UserWarning, - ) - from web3.pm import PM # noqa: F811 - - self.attach_modules({"_pm": PM}) - class Web3(BaseWeb3): # mypy types diff --git a/web3/pm.py b/web3/pm.py deleted file mode 100644 index 562123cc31..0000000000 --- a/web3/pm.py +++ /dev/null @@ -1,602 +0,0 @@ -from abc import ( - ABC, - abstractmethod, -) -import json -from pathlib import ( - Path, -) -from typing import ( - Any, - Dict, - Iterable, - NamedTuple, - Tuple, - Type, - TypeVar, - Union, - cast, -) - -from eth_typing import ( - URI, - Address, - ChecksumAddress, - ContractName, - Manifest, -) -from eth_utils import ( - is_canonical_address, - is_checksum_address, - to_checksum_address, - to_text, - to_tuple, -) - -from ens import ( - ENS, -) -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.exceptions import ( - EthPMException, - ManifestValidationError, -) -from ethpm.uri import ( - is_supported_content_addressed_uri, - resolve_uri_contents, -) -from ethpm.validation.manifest import ( - validate_manifest_against_schema, - validate_raw_manifest_format, -) -from ethpm.validation.package import ( - validate_package_name, - validate_package_version, -) -from web3 import ( - Web3, -) -from web3._utils.ens import ( - is_ens_name, -) -from web3.exceptions import ( - InvalidAddress, - NameNotFound, -) -from web3.module import ( - Module, -) - -# Package Management is still in alpha, and its API is likely to change, so it -# is not automatically available on a web3 instance. To use the `PM` module, -# please enable the package management API on an individual web3 instance. -# -# >>> from web3 import Web3, IPCProvider -# >>> w3 = Web3(IPCProvider(...)) -# >>> w3.pm -# AttributeError: The Package Management feature is disabled by default ... -# >>> w3.enable_unstable_package_management_api() -# >>> w3.pm -# - -T = TypeVar("T") - - -class ReleaseData(NamedTuple): - package_name: str - version: str - manifest_uri: URI - - -class ERC1319Registry(ABC): - """ - The ERC1319Registry class is a base class for all registry implementations - to inherit from. It defines the methods specified in - `ERC 1319 `__. All of these - methods are prefixed with an underscore, since they are not intended to be - accessed directly, but rather through the methods on ``web3.pm``. - They are unlikely to change, but must be implemented in a `ERC1319Registry` - subclass in order to be compatible with the `PM` module. Any custom - methods (eg. not defined in ERC1319) in a subclass should *not* be - prefixed with an underscore. - - All of these methods must be implemented in any subclass in order to work - with `web3.pm.PM`. Any implementation specific logic should be - handled in a subclass. - """ - - @abstractmethod - def __init__(self, address: Address, w3: Web3) -> None: - """ - Initializes the class with the on-chain address of the registry, and a web3 - instance connected to the chain where the registry can be found. - - Must set the following properties... - - * ``self.registry``: A `web3.contract` instance of the target registry. - * ``self.address``: The address of the target registry. - * ``self.w3``: The *web3* instance connected to the chain where the - registry can be found. - """ - pass - - # - # Write API - # - - @abstractmethod - def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: - """ - Returns the releaseId created by successfully adding a release to the registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to - any versioning scheme. - * ``manifest_uri``: URI location of a manifest which details the - release contents - """ - pass - - # - # Read API - # - - @abstractmethod - def _get_package_name(self, package_id: bytes) -> str: - """ - Returns the package name associated with the given package id, if the - package id exists on the connected registry. - - * Parameters: - * ``package_id``: 32 byte package identifier. - """ - pass - - @abstractmethod - def _get_all_package_ids(self) -> Iterable[bytes]: - """ - Returns a tuple containing all of the package ids found on the - connected registry. - """ - pass - - @abstractmethod - def _get_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 bytes release id associated with the given - package name and version, if the release exists on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to - any versioning scheme. - """ - pass - - @abstractmethod - def _get_all_release_ids(self, package_name: str) -> Iterable[bytes]: - """ - Returns a tuple containing all of the release ids belonging to the - given package name, if the package has releases on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - """ - pass - - @abstractmethod - def _get_release_data(self, release_id: bytes) -> ReleaseData: - """ - Returns a tuple containing (package_name, version, manifest_uri) for the - given release id, if the release exists on the connected registry. - - * Parameters: - * ``release_id``: 32 byte release identifier. - """ - pass - - @abstractmethod - def _generate_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 byte release identifier that *would* be associated with the given - package name and version according to the registry's hashing mechanism. - The release *does not* have to exist on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to - any versioning scheme. - """ - pass - - @abstractmethod - def _num_package_ids(self) -> int: - """ - Returns the number of packages that exist on the connected registry. - """ - pass - - @abstractmethod - def _num_release_ids(self, package_name: str) -> int: - """ - Returns the number of releases found on the connected registry, - that belong to the given package name. - - * Parameters: - * ``package_name``: Valid package name according the spec. - """ - pass - - @classmethod - @abstractmethod - def deploy_new_instance(cls: Type[T], w3: Web3) -> T: - """ - Class method that returns a newly deployed instance of ERC1319Registry. - - * Parameters: - * ``w3``: Web3 instance on which to deploy the new registry. - """ - pass - - -BATCH_SIZE = 100 - - -class SimpleRegistry(ERC1319Registry): - """ - This class represents an instance of the `Solidity Reference Registry implementation - `__. - """ - - def __init__(self, address: ChecksumAddress, w3: Web3) -> None: - abi = get_simple_registry_manifest()["contractTypes"]["PackageRegistry"]["abi"] - self.registry = w3.eth.contract(address=address, abi=abi) - self.address = address - self.w3 = w3 - - def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: - tx_hash = self.registry.functions.release( - package_name, version, manifest_uri - ).transact() - self.w3.eth.wait_for_transaction_receipt(tx_hash) - return self._get_release_id(package_name, version) - - def _get_package_name(self, package_id: bytes) -> str: - package_name = self.registry.functions.getPackageName(package_id).call() - return package_name - - @to_tuple - def _get_all_package_ids(self) -> Iterable[bytes]: - num_packages = self._num_package_ids() - pointer = 0 - while pointer < num_packages: - new_ids, new_pointer = self.registry.functions.getAllPackageIds( - pointer, (pointer + BATCH_SIZE) - ).call() - if not new_pointer > pointer: - break - yield from reversed(new_ids) - pointer = new_pointer - - def _get_release_id(self, package_name: str, version: str) -> bytes: - return self.registry.functions.getReleaseId(package_name, version).call() - - @to_tuple - def _get_all_release_ids(self, package_name: str) -> Iterable[bytes]: - num_releases = self._num_release_ids(package_name) - pointer = 0 - while pointer < num_releases: - new_ids, new_pointer = self.registry.functions.getAllReleaseIds( - package_name, pointer, (pointer + BATCH_SIZE) - ).call() - if not new_pointer > pointer: - break - yield from reversed(new_ids) - pointer = new_pointer - - def _get_release_data(self, release_id: bytes) -> ReleaseData: - name, version, uri = self.registry.functions.getReleaseData(release_id).call() - return ReleaseData(name, version, uri) - - def _generate_release_id(self, package_name: str, version: str) -> bytes: - return self.registry.functions.generateReleaseId(package_name, version).call() - - def _num_package_ids(self) -> int: - return self.registry.functions.numPackageIds().call() - - def _num_release_ids(self, package_name: str) -> int: - return self.registry.functions.numReleaseIds(package_name).call() - - @classmethod - def deploy_new_instance(cls, w3: Web3) -> "SimpleRegistry": - manifest = get_simple_registry_manifest() - registry_package = Package(manifest, w3) - registry_factory = registry_package.get_contract_factory( - ContractName("PackageRegistry") - ) - tx_hash = registry_factory.constructor().transact() - tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - return cls(tx_receipt["contractAddress"], w3) - - -class PM(Module): - """ - The PM module will work with any subclass of ``ERC1319Registry``, - tailored to a particular implementation of - `ERC1319 `__, set as - its ``registry`` attribute. - """ - - # mypy types - w3: "Web3" - - def get_package_from_manifest(self, manifest: Manifest) -> Package: - """ - Returns a `Package `__ # noqa: E501 - instance built with the given manifest. - - * Parameters: - * ``manifest``: A dict representing a valid manifest - """ - return Package(manifest, self.w3) - - def get_package_from_uri(self, manifest_uri: URI) -> Package: - """ - Returns a `Package `__ # noqa: E501 - instance built with the Manifest stored at the URI. - If you want to use a specific IPFS backend, set ``ETHPM_IPFS_BACKEND_CLASS`` - to your desired backend. Defaults to Infura IPFS backend. - - * Parameters: - * ``uri``: Must be a valid content-addressed URI - """ - return Package.from_uri(manifest_uri, self.w3) - - def get_local_package(self, package_name: str, ethpm_dir: Path = None) -> Package: - """ - Returns a `Package `__ # noqa: E501 - instance built with the Manifest found at the package name in your local ethpm_dir. - - * Parameters: - * ``package_name``: Must be the name of a package installed locally. - * ``ethpm_dir``: Path pointing to the target ethpm directory (optional). - """ - if not ethpm_dir: - ethpm_dir = Path.cwd() / "_ethpm_packages" - - if not ethpm_dir.name == "_ethpm_packages" or not ethpm_dir.is_dir(): - raise EthPMException( - f"{ethpm_dir} is not a valid ethPM packages directory." - ) - - local_packages = [pkg.name for pkg in ethpm_dir.iterdir() if pkg.is_dir()] - if package_name not in local_packages: - raise EthPMException( - f"Package: {package_name} not found in {ethpm_dir}. " - f"Available packages include: {local_packages}." - ) - - target_manifest = json.loads( - (ethpm_dir / package_name / "manifest.json").read_text() - ) - return self.get_package_from_manifest(target_manifest) - - def set_registry(self, address: Union[Address, ChecksumAddress, ENS]) -> None: - """ - Sets the current registry used in ``web3.pm`` functions that read/write - to an on-chain registry. This method accepts checksummed/canonical - addresses or ENS names. Addresses must point to an on-chain instance - of an ERC1319 registry implementation. - - To use an ENS domain as the address, make sure a valid ENS instance - set as ``web3.ens``. - - * Parameters: - * ``address``: Address of on-chain Registry. - """ - if is_canonical_address(address): - addr_string = to_text(address) - self.registry = SimpleRegistry(to_checksum_address(addr_string), self.w3) - elif is_checksum_address(address): - self.registry = SimpleRegistry(cast(ChecksumAddress, address), self.w3) - elif is_ens_name(address): - self._validate_set_ens() - ens = cast(ENS, self.w3.ens) - addr_lookup = ens.address(str(address)) - if not addr_lookup: - raise NameNotFound( - f"No address found after ENS lookup for name: {address!r}." - ) - self.registry = SimpleRegistry(addr_lookup, self.w3) - else: - raise EthPMException( - "Expected a canonical/checksummed address or ENS name for the address, " - f"instead received {type(address)}." - ) - - def deploy_and_set_registry(self) -> ChecksumAddress: - """ - Returns the address of a freshly deployed instance of `SimpleRegistry` - and sets the newly deployed registry as the active registry on - ``web3.pm.registry``. - - To tie your registry to an ENS name, use web3's ENS module, ie. - - .. code-block:: python - - w3.ens.setup_address(ens_name, w3.pm.registry.address) - """ - self.registry = SimpleRegistry.deploy_new_instance(self.w3) - return to_checksum_address(self.registry.address) - - def release_package( - self, package_name: str, version: str, manifest_uri: URI - ) -> bytes: - """ - Returns the release id generated by releasing a package on the current registry. - Requires ``web3.PM`` to have a registry set. Requires - ``web3.eth.default_account`` to be the registry owner. - - * Parameters: - * ``package_name``: Must be a valid package name, matching the - given manifest. - * ``version``: Must be a valid package version, matching the given manifest. - * ``manifest_uri``: Must be a valid content-addressed URI. Currently, - only IPFS and Github content-addressed URIs are - supported. - - """ - validate_is_supported_manifest_uri(manifest_uri) - raw_manifest = to_text(resolve_uri_contents(manifest_uri)) - validate_raw_manifest_format(raw_manifest) - manifest = json.loads(raw_manifest) - validate_manifest_against_schema(manifest) - if package_name != manifest["name"]: - raise ManifestValidationError( - f"Provided package name: {package_name} does not match the package " - f"name found in the manifest: {manifest['name']}." - ) - - if version != manifest["version"]: - raise ManifestValidationError( - f"Provided package version: {version} does not match the package " - f"version found in the manifest: {manifest['version']}." - ) - - self._validate_set_registry() - return self.registry._release(package_name, version, manifest_uri) - - @to_tuple - def get_all_package_names(self) -> Iterable[str]: - """ - Returns a tuple containing all the package names - available on the current registry. - """ - self._validate_set_registry() - package_ids = self.registry._get_all_package_ids() - for package_id in package_ids: - yield self.registry._get_package_name(package_id) - - def get_package_count(self) -> int: - """ - Returns the number of packages available on the current registry. - """ - self._validate_set_registry() - return self.registry._num_package_ids() - - def get_release_count(self, package_name: str) -> int: - """ - Returns the number of releases of the given package name - available on the current registry. - """ - validate_package_name(package_name) - self._validate_set_registry() - return self.registry._num_release_ids(package_name) - - def get_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 byte identifier of a release for the given package - name and version, if they are available on the current registry. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - return self.registry._get_release_id(package_name, version) - - @to_tuple - def get_all_package_releases(self, package_name: str) -> Iterable[Tuple[str, str]]: - """ - Returns a tuple of release data (version, manifest_ur) for every release of the - given package name available on the current registry. - """ - validate_package_name(package_name) - self._validate_set_registry() - release_ids = self.registry._get_all_release_ids(package_name) - for release_id in release_ids: - release_data = self.registry._get_release_data(release_id) - yield (release_data.version, release_data.manifest_uri) - - def get_release_id_data(self, release_id: bytes) -> ReleaseData: - """ - Returns ``(package_name, version, manifest_uri)`` associated with the given - release id, *if* it is available on the current registry. - - * Parameters: - * ``release_id``: 32 byte release identifier - """ - self._validate_set_registry() - return self.registry._get_release_data(release_id) - - def get_release_data(self, package_name: str, version: str) -> ReleaseData: - """ - Returns ``(package_name, version, manifest_uri)`` associated with the given - package name and version, *if* they are published to the currently set registry. - - * Parameters: - * ``name``: Must be a valid package name. - * ``version``: Must be a valid package version. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - release_id = self.registry._get_release_id(package_name, version) - return self.get_release_id_data(release_id) - - def get_package(self, package_name: str, version: str) -> Package: - """ - Returns a ``Package`` instance, generated by the ``manifest_uri`` - associated with the given package name and version, if they are - published to the currently set registry. - - * Parameters: - * ``name``: Must be a valid package name. - * ``version``: Must be a valid package version. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - release_data = self.get_release_data(package_name, version) - return self.get_package_from_uri(URI(release_data.manifest_uri)) - - def _validate_set_registry(self) -> None: - try: - self.registry - except AttributeError: - raise EthPMException( - "web3.pm does not have a set registry. " - "Please set registry with either: " - "web3.pm.set_registry(address) or " - "web3.pm.deploy_and_set_registry()" - ) - if not isinstance(self.registry, ERC1319Registry): - raise EthPMException( - "web3.pm requires an instance of a subclass of ERC1319Registry " - "to be set as the web3.pm.registry attribute. Instead found: " - f"{type(self.registry)}." - ) - - def _validate_set_ens(self) -> None: - if not self.w3: - raise InvalidAddress( - "Could not look up ENS address because no web3 connection available" - ) - elif not self.w3.ens: - raise InvalidAddress( - "Could not look up ENS address because web3.ens is not set up" - ) - - -def get_simple_registry_manifest() -> Dict[str, Any]: - return json.loads((ASSETS_DIR / "simple-registry" / "v3.json").read_text()) - - -def validate_is_supported_manifest_uri(uri: URI) -> None: - if not is_supported_content_addressed_uri(uri): - raise ManifestValidationError( - f"URI: {uri} is not a valid content-addressed URI. " - "Currently only IPFS and Github content-addressed URIs are supported." - ) diff --git a/web3/tools/__init__.py b/web3/tools/__init__.py deleted file mode 100644 index 7cc14d91e7..0000000000 --- a/web3/tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .pytest_ethereum import ( - deployer, - linker, -) diff --git a/web3/tools/pytest_ethereum/README.md b/web3/tools/pytest_ethereum/README.md deleted file mode 100644 index 7a43e989ae..0000000000 --- a/web3/tools/pytest_ethereum/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Overview - -These `pytest-ethereum` tools include a few utilities to simplify handling EthPM packages. Specifically, deploying and linking contracts that are contained within an EthPM package. When the `py-ethpm` codebase was [ported](https://github.com/ethereum/web3.py/pull/1379) into `web3` to remove a circular dependency between `ethpm` & `web3`, these utilities were simultaneously ported over from [pytest-ethereum](https://github.com/ethereum/pytest-ethereum), to avoid a similar circular dependency. - -Complete documentation for how to use these utilities can be found on the [pytest-ethereum docs](https://pytest-ethereum.readthedocs.io/en/latest/overview.html). diff --git a/web3/tools/pytest_ethereum/__init__.py b/web3/tools/pytest_ethereum/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/web3/tools/pytest_ethereum/_utils.py b/web3/tools/pytest_ethereum/_utils.py deleted file mode 100644 index 47a06fbc46..0000000000 --- a/web3/tools/pytest_ethereum/_utils.py +++ /dev/null @@ -1,145 +0,0 @@ -from typing import ( - Any, - Dict, - Iterable, - List, - Tuple, -) - -from eth_typing import ( - URI, - Address, - ContractName, - Manifest, -) -from eth_utils import ( - to_dict, - to_hex, - to_list, -) -from eth_utils.toolz import ( - assoc, - assoc_in, - dissoc, -) - -from ethpm import ( - Package, -) -from ethpm.uri import ( - check_if_chain_matches_chain_uri, -) -from web3 import ( - Web3, -) -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) -from web3.types import ( - TxReceipt, -) - - -def pluck_matching_uri(deployment_data: Dict[URI, Dict[str, str]], w3: Web3) -> URI: - """ - Return any blockchain uri that matches w3-connected chain, if one - is present in the deployment data keys. - """ - for uri in deployment_data.keys(): - if check_if_chain_matches_chain_uri(w3, uri): - return uri - raise LinkerError( - "No matching blockchain URI found in deployment_data: " - f"{list(deployment_data.keys())}, for w3 instance: {w3.__repr__()}." - ) - - -def contains_matching_uri(deployment_data: Dict[str, Dict[str, str]], w3: Web3) -> bool: - """ - Returns true if any blockchain uri in deployment data matches - w3-connected chain. - """ - for uri in deployment_data.keys(): - if check_if_chain_matches_chain_uri(w3, uri): - return True - return False - - -def insert_deployment( - package: Package, - deployment_name: str, - deployment_data: Dict[str, str], - latest_block_uri: URI, -) -> Manifest: - """ - Returns a new manifest. If a matching chain uri is found - in the old manifest, it will update the chain uri along - with the new deployment data. If no match, it will simply add - the new chain uri and deployment data. - """ - old_deployments_data = package.manifest.get("deployments") - if old_deployments_data and contains_matching_uri(old_deployments_data, package.w3): - old_chain_uri = pluck_matching_uri(old_deployments_data, package.w3) - old_deployments_chain_data = old_deployments_data[old_chain_uri] - # Replace specific on-chain deployment (i.e. deployment_name) - new_deployments_chain_data_init = dissoc( - old_deployments_chain_data, deployment_name - ) - new_deployments_chain_data = { - **new_deployments_chain_data_init, - **{deployment_name: deployment_data}, - } - # Replace all on-chain deployments - new_deployments_data_init = dissoc( - old_deployments_data, "deployments", old_chain_uri - ) - new_deployments_data = { - **new_deployments_data_init, - **{latest_block_uri: new_deployments_chain_data}, - } - return assoc(package.manifest, "deployments", new_deployments_data) - - return assoc_in( - package.manifest, - ("deployments", latest_block_uri, deployment_name), - deployment_data, - ) - - -@to_dict -def create_deployment_data( - contract_name: ContractName, - new_address: Address, - tx_receipt: TxReceipt, - link_refs: List[Dict[str, Any]] = None, -) -> Iterable[Tuple[str, Any]]: - yield "contractType", contract_name - yield "address", new_address - yield "transaction", to_hex(tx_receipt["transactionHash"]) - yield "block", to_hex(tx_receipt["blockHash"]) - if link_refs: - yield "runtimeBytecode", {"linkDependencies": create_link_dep(link_refs)} - - -@to_list -def create_link_dep(link_refs: List[Dict[str, Any]]) -> Iterable[Dict[str, Any]]: - for link_ref in link_refs: - yield { - "offsets": link_ref["offsets"], - "type": "reference", - "value": link_ref["name"], - } - - -def get_deployment_address(linked_type: str, package: Package) -> Address: - """ - Return the address of a linked_type found in a package's manifest deployments. - """ - try: - deployment_address = package.deployments.get(linked_type)["address"] - except KeyError: - raise LinkerError( - f"Package data does not contain a valid deployment of {linked_type} on the " - "current w3-connected chain." - ) - return deployment_address diff --git a/web3/tools/pytest_ethereum/deployer.py b/web3/tools/pytest_ethereum/deployer.py deleted file mode 100644 index e3e95c9793..0000000000 --- a/web3/tools/pytest_ethereum/deployer.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import ( - Any, - Callable, - Dict, -) - -from eth_typing import ( - ContractName, -) - -from ethpm import ( - Package, -) -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - linker, -) - - -class Deployer: - def __init__(self, package: Package) -> None: - if not isinstance(package, Package): - raise TypeError( - f"Expected a Package object, instead received {type(package)}." - ) - self.package = package - self.strategies = {} # type: Dict[str, Callable[[Package], Package]] - - def deploy(self, contract_type: ContractName, *args: Any, **kwargs: Any) -> Package: - factory = self.package.get_contract_factory(contract_type) - if contract_type in self.strategies: - strategy = self.strategies[contract_type] - return strategy(self.package) - if factory.needs_bytecode_linking: - raise DeployerError( - "Unable to deploy an unlinked factory. " - "Please register a strategy for this contract type." - ) - strategy = linker(deploy(contract_type, *args, **kwargs)) - return strategy(self.package) - - def register_strategy( - self, contract_type: ContractName, strategy: Callable[[Package], Package] - ) -> None: - self.strategies[contract_type] = strategy diff --git a/web3/tools/pytest_ethereum/exceptions.py b/web3/tools/pytest_ethereum/exceptions.py deleted file mode 100644 index c474ebcf19..0000000000 --- a/web3/tools/pytest_ethereum/exceptions.py +++ /dev/null @@ -1,22 +0,0 @@ -class PytestEthereumError(Exception): - """ - Base class for all Pytest-Ethereum errors. - """ - - pass - - -class DeployerError(PytestEthereumError): - """ - Raised when the Deployer is unable to deploy a contract type. - """ - - pass - - -class LinkerError(PytestEthereumError): - """ - Raised when the Linker is unable to link two contract types. - """ - - pass diff --git a/web3/tools/pytest_ethereum/linker.py b/web3/tools/pytest_ethereum/linker.py deleted file mode 100644 index 79e3b1d3d9..0000000000 --- a/web3/tools/pytest_ethereum/linker.py +++ /dev/null @@ -1,128 +0,0 @@ -import logging -from typing import ( - Any, - Callable, - Dict, -) - -from eth_typing import ( - ContractName, -) -from eth_utils import ( - to_checksum_address, - to_hex, -) -from eth_utils.toolz import ( - assoc_in, - curry, - pipe, -) - -from ethpm import ( - Package, -) -from ethpm.uri import ( - create_latest_block_uri, -) -from web3.tools.pytest_ethereum._utils import ( - create_deployment_data, - get_deployment_address, - insert_deployment, -) -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) - -logger = logging.getLogger("pytest_ethereum.linker") - - -def linker(*args: Callable[..., Any]) -> Callable[..., Any]: - return _linker(args) - - -@curry -def _linker(operations: Callable[..., Any], package: Package) -> Callable[..., Package]: - return pipe(package, *operations) - - -def deploy( - contract_name: str, *args: Any, transaction: Dict[str, Any] = None -) -> Callable[..., Package]: - """ - Return a newly created package and contract address. - Will deploy the given contract_name, if data exists in package. If - a deployment is found on the current w3 instance, it will return that deployment - rather than creating a new instance. - """ - return _deploy(contract_name, args, transaction) - - -@curry -def _deploy( - contract_name: ContractName, - args: Any, - transaction: Dict[str, Any], - package: Package, -) -> Package: - # Deploy new instance - factory = package.get_contract_factory(contract_name) - if not factory.linked_references and factory.unlinked_references: - raise LinkerError( - f"Contract factory: {contract_name} is missing runtime link references, " - "which are necessary to populate manifest deployments that have a link " - "reference. If using the builder tool, use " - "`contract_type(..., runtime_bytecode=True)`." - ) - tx_hash = factory.constructor(*args).transact(transaction) - tx_receipt = package.w3.eth.wait_for_transaction_receipt(tx_hash) - # Create manifest copy with new deployment instance - latest_block_uri = create_latest_block_uri(package.w3, 0) - deployment_data = create_deployment_data( - contract_name, - to_checksum_address(tx_receipt["contractAddress"]), - tx_receipt, - factory.linked_references, - ) - manifest = insert_deployment( - package, contract_name, deployment_data, latest_block_uri - ) - logger.info(f"{contract_name} deployed.") - return Package(manifest, package.w3) - - -@curry -def link(contract: ContractName, linked_type: str, package: Package) -> Package: - """ - Return a new package, created with a new manifest after applying the linked type - reference to the contract factory. - """ - deployment_address = get_deployment_address(linked_type, package) - unlinked_factory = package.get_contract_factory(contract) - if not unlinked_factory.needs_bytecode_linking: - raise LinkerError( - f"Contract factory: {unlinked_factory.__repr__()} does not need " - "bytecode linking, so it is not a valid contract type for link()" - ) - linked_factory = unlinked_factory.link_bytecode({linked_type: deployment_address}) - # todo replace runtime_bytecode in manifest - manifest = assoc_in( - package.manifest, - ("contractTypes", contract, "deploymentBytecode", "bytecode"), - to_hex(linked_factory.bytecode), - ) - logger.info( - f"{contract} linked to {linked_type} at address " - f"{to_checksum_address(deployment_address)}." - ) - return Package(manifest, package.w3) - - -@curry -def run_python(callback_fn: Callable[..., None], package: Package) -> Package: - """ - Return the unmodified package, after performing any user-defined - callback function on the contracts in the package. - """ - callback_fn(package) - logger.info(f"{callback_fn.__name__} python function ran.") - return package diff --git a/web3/tools/pytest_ethereum/plugins.py b/web3/tools/pytest_ethereum/plugins.py deleted file mode 100644 index 7b9e373909..0000000000 --- a/web3/tools/pytest_ethereum/plugins.py +++ /dev/null @@ -1,33 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest -from typing import ( - Callable, -) - -from ethpm import ( - Package, -) -from web3 import ( - Web3, -) -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) - - -@pytest.fixture -def deployer(w3: Web3) -> Callable[[Path], Deployer]: - """ - Returns a `Deployer` instance composed from a `Package` instance - generated from the manifest located at the provided `path` folder. - """ - - def _deployer(path: Path) -> Deployer: - manifest = json.loads(path.read_text()) - package = Package(manifest, w3) - return Deployer(package) - - return _deployer