Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update registry uri #169

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/uri_backends.rst
Expand Up @@ -80,16 +80,22 @@ way through the EIP process)

::

scheme://authority/package-name?version=x.x.x
scheme://address:chain_id/package-name?version=x.x.x

- URI must be a string type
- ``scheme``: ``ercxxx``
- ``authority``: Must be a valid ENS domain or a valid checksum address
- ``scheme``: ``erc1319``
- ``address``: 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. Currently supported chains include...
- 1: Mainnet
- 3: Ropsten
- 4: Rinkeby
- 5: Goerli
- 42: Kovan
- ``package-name``: Must conform to the package-name as specified in
the
`EthPM-Spec <http://ethpm-spec.readthedocs.io/en/latest/package-spec.html#package-name>`__.
- ``version``: The URI escaped version string, *should* conform to the
`semver <http://semver.org/>`__ version numbering specification.

i.e. ``ercxxx://packages.zeppelinos.eth/owned?version=1.0.0``
i.e. ``erc1319://packages.zeppelinos.eth:1/owned?version=1.0.0``
14 changes: 12 additions & 2 deletions ethpm/_utils/chains.py
@@ -1,11 +1,12 @@
import re
from typing import Tuple
from typing import Any, Tuple
from urllib import parse

from eth_typing import URI
from eth_utils import add_0x_prefix, remove_0x_prefix
from eth_utils import add_0x_prefix, is_integer, remove_0x_prefix
from web3 import Web3

from ethpm.constants import SUPPORTED_CHAIN_IDS

def get_genesis_block_hash(web3: Web3) -> str:
return web3.eth.getBlock(0)["hash"]
Expand Down Expand Up @@ -81,3 +82,12 @@ def create_BIP122_uri(

def create_block_uri(chain_id: str, block_identifier: str) -> URI:
return create_BIP122_uri(chain_id, "block", remove_0x_prefix(block_identifier))


def is_supported_chain_id(chain_id: Any) -> bool:
if not is_integer(chain_id):
return False

if chain_id not in SUPPORTED_CHAIN_IDS.keys():
return False
return True
14 changes: 9 additions & 5 deletions ethpm/backends/registry.py
Expand Up @@ -9,12 +9,12 @@
from ethpm._utils.registry import fetch_standard_registry_abi
from ethpm.backends.base import BaseURIBackend
from ethpm.constants import INFURA_API_KEY
from ethpm.exceptions import ValidationError
from ethpm.exceptions import CannotHandleURI, ValidationError
from ethpm.validation.uri import validate_registry_uri

# TODO: Update registry ABI once ERC is finalized.
REGISTRY_ABI = fetch_standard_registry_abi()
RegistryURI = namedtuple("RegistryURI", ["auth", "name", "version"])
RegistryURI = namedtuple("RegistryURI", ["address", "chain_id", "name", "version"])


class RegistryURIBackend(BaseURIBackend):
Expand All @@ -39,7 +39,11 @@ def fetch_uri_contents(self, uri: str) -> URI:
"""
Return content-addressed URI stored at registry URI.
"""
address, pkg_name, pkg_version = parse_registry_uri(uri)
address, chain_id, pkg_name, pkg_version = parse_registry_uri(uri)
if chain_id != '1':
raise CannotHandleURI(
"Currently only mainnet registry uris are supported."
)
self.w3.enable_unstable_package_management_api()
self.w3.pm.set_registry(address)
_, _, manifest_uri = self.w3.pm.get_release_data(pkg_name, pkg_version)
Expand All @@ -65,7 +69,7 @@ def parse_registry_uri(uri: str) -> RegistryURI:
"""
validate_registry_uri(uri)
parsed_uri = parse.urlparse(uri)
authority = parsed_uri.netloc
address, chain_id = parsed_uri.netloc.split(":")
pkg_name = parsed_uri.path.strip("/")
pkg_version = parsed_uri.query.lstrip("version=").strip("/")
return RegistryURI(authority, pkg_name, pkg_version)
return RegistryURI(address, chain_id, pkg_name, pkg_version)
11 changes: 9 additions & 2 deletions ethpm/constants.py
@@ -1,5 +1,4 @@
# TODO update once registry standard eip has been approved
REGISTRY_URI_SCHEME = "ercxxx"
REGISTRY_URI_SCHEME = "erc1319"

PACKAGE_NAME_REGEX = "[a-zA-Z][-_a-zA-Z0-9]{0,255}"

Expand All @@ -14,3 +13,11 @@
INFURA_GATEWAY_MULTIADDR = "/dns4/ipfs.infura.io/tcp/5001/https/"

GITHUB_API_AUTHORITY = "api.github.com"

SUPPORTED_CHAIN_IDS = {
1: "mainnet",
3: "ropsten",
4: "rinkeby",
5: "goerli",
42: "kovan",
}
25 changes: 22 additions & 3 deletions ethpm/validation/uri.py
Expand Up @@ -2,10 +2,11 @@
from typing import List
from urllib import parse

from eth_utils import is_checksum_address, to_bytes, to_text
from eth_utils import is_checksum_address, to_bytes, to_int, to_text
from web3 import Web3

from ethpm._utils.ipfs import is_ipfs_uri
from ethpm._utils.chains import is_supported_chain_id
from ethpm._utils.registry import is_ens_domain
from ethpm.constants import REGISTRY_URI_SCHEME
from ethpm.exceptions import ValidationError
Expand Down Expand Up @@ -43,8 +44,26 @@ def validate_registry_uri_authority(auth: str) -> None:
Raise an exception if the authority is not a valid ENS domain
or a valid checksummed contract address.
"""
if is_ens_domain(auth) is False and not is_checksum_address(auth):
raise ValidationError(f"{auth} is not a valid registry URI authority.")
try:
address, chain_id = auth.split(':')
except ValueError:
raise ValidationError(
f"{auth} is not a valid registry URI authority. "
"Please try again with a valid registry URI."
)

if is_ens_domain(address) is False and not is_checksum_address(address):
raise ValidationError(
f"{auth} is not a valid registry address. "
"Please try again with a valid registry URI."
)

if not is_supported_chain_id(to_int(text=chain_id)):
raise ValidationError(
f"Chain ID: {chain_id} is not supported. Supported chain ids include: "
"1 (mainnet), 3 (ropsten), 4 (rinkeby), 5 (goerli) and 42 (kovan). "
"Please try again with a valid registry URI."
)


def validate_registry_uri_scheme(scheme: str) -> None:
Expand Down
6 changes: 3 additions & 3 deletions tests/ethpm/_utils/test_backend_utils.py
Expand Up @@ -17,7 +17,7 @@
"ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/",
(InfuraIPFSBackend, LocalIPFSBackend),
),
("ercXXX://packages.zeppelinos.eth/erc20?version=1.0.0", ()),
("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", ()),
),
)
def test_get_resolvable_backends_for_supported_uris(dummy_ipfs_backend, uri, backends):
Expand All @@ -28,7 +28,7 @@ def test_get_resolvable_backends_for_supported_uris(dummy_ipfs_backend, uri, bac
@pytest.mark.parametrize(
"uri,backends",
(
("ercXXX://packages.zeppelinos.eth/erc20?version=1.0.0", (RegistryURIBackend,)),
("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", (RegistryURIBackend,)),
("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", ()),
),
)
Expand All @@ -46,7 +46,7 @@ def test_get_translatable_backends_for_supported_uris(
# filesystem
"file:///path_to_erc20.json",
# invalid registry URI scheme
"erc1128://packages.zeppelinos.eth/erc20/v1.0.0",
"erc1128://packages.zeppelinos.eth:1/erc20/v1.0.0",
# swarm
"bzz://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d",
"bzz-immutable:://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d",
Expand Down
22 changes: 21 additions & 1 deletion tests/ethpm/_utils/test_chain_utils.py
@@ -1,6 +1,6 @@
import pytest

from ethpm._utils.chains import is_BIP122_block_uri, parse_BIP122_uri
from ethpm._utils.chains import is_BIP122_block_uri, is_supported_chain_id, parse_BIP122_uri

HASH_A = "0x1234567890123456789012345678901234567890123456789012345678901234"
HASH_A_NO_PREFIX = "1234567890123456789012345678901234567890123456789012345678901234"
Expand Down Expand Up @@ -36,3 +36,23 @@ def test_parse_BIP122_uri(value, expected_resource_type):
assert chain_id == HASH_A
assert resource_type == expected_resource_type
assert resource_identifier == HASH_B


@pytest.mark.parametrize(
"chain_id,expected",
(
(1, True),
(3, True),
(4, True),
(5, True),
(42, True),
(2, 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
70 changes: 36 additions & 34 deletions tests/ethpm/_utils/test_registry_utils.py
Expand Up @@ -7,18 +7,18 @@
@pytest.mark.parametrize(
"uri",
(
("ercxxx://zeppelinos.eth/erc20/"),
("ercXXX://zeppelinos.eth/erc20/"),
("ercXXX://zeppelinos.eth/erc20//"),
("ercXXX://zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX://zeppelinos.eth/erc20?version=1.0.0/"),
("ercXXX://packages.zeppelinos.eth/erc20?version="),
("ercXXX://packages.zeppelinos.eth/erc20?version=/"),
("ercXXX://packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX://packages.zeppelinos.eth/erc20?version=1.0.0/"),
("ercXXX://packages.ethereum.eth/greeter?version=%3E%3D1.0.2%2C%3C2"),
("ercXXX://0xd3CdA913deB6f67967B99D67aCDFa1712C293601/erc20?version=1.0.0"),
("ercXXX://0xd3CdA913deB6f67967B99D67aCDFa1712C293601/erc20?version=1.0.0/"),
("erc1319://zeppelinos.eth:1/erc20/"),
("erc1319://zeppelinos.eth:1/erc20/"),
("erc1319://zeppelinos.eth:1/erc20//"),
("erc1319://zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319://zeppelinos.eth:1/erc20?version=1.0.0/"),
("erc1319://packages.zeppelinos.eth:1/erc20?version="),
("erc1319://packages.zeppelinos.eth:1/erc20?version=/"),
("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0/"),
("erc1319://packages.ethereum.eth:1/greeter?version=%3E%3D1.0.2%2C%3C2"),
("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20?version=1.0.0"),
("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20?version=1.0.0/"),
),
)
def test_is_registry_uri_validates(uri):
Expand All @@ -29,34 +29,36 @@ def test_is_registry_uri_validates(uri):
"uri",
(
# invalid authority
("ercXXX://packages.zeppelinos.com/erc20?version=1.0.0"),
("ercXXX://package.manager.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX://packageszeppelinoseth/erc20?version=1.0.0"),
("ercXXX://0xd3cda913deb6f67967b99d67acdfa1712c293601/erc20?version=1.0.0"),
("erc1319://zeppelinos.eth/erc20?version=1.0.0"),
("erc1319://zeppelinos.eth:333/erc20?version=1.0.0"),
("erc1319://packages.zeppelinos.com:1/erc20?version=1.0.0"),
("erc1319://package.manager.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319://packageszeppelinoseth:1/erc20?version=1.0.0"),
("erc1319://0xd3cda913deb6f67967b99d67acdfa1712c293601:1/erc20?version=1.0.0"),
# invalid package name
("ercXXX://packages.zeppelinos.eth/"),
("ercXXX://packages.zeppelinos.eth///"),
("ercXXX://packages.zeppelinos.eth/?version=1.0.0"),
("ercXXX://packages.zeppelinos.eth/!rc20?version=1.0.0"),
("erc1319://packages.zeppelinos.eth:1/"),
("erc1319://packages.zeppelinos.eth:1///"),
("erc1319://packages.zeppelinos.eth:1/?version=1.0.0"),
("erc1319://packages.zeppelinos.eth:1/!rc20?version=1.0.0"),
# invalid version param
("ercXXX://zeppelinos.eth/erc20?versions=1.0.0"),
("ercXXX://zeppelinos.eth/erc20?version1.0.0"),
("erc1319://zeppelinos.eth:1/erc20?versions=1.0.0"),
("erc1319://zeppelinos.eth:1/erc20?version1.0.0"),
# malformed
("ercXXXpackageszeppelinosetherc20version1.0.0"),
("ercXXX:packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX:packages.zeppelinos.eth/erc20?version=1.0.0/"),
("ercXXX:/packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX:/packages.zeppelinos.eth/erc20?version=1.0.0/"),
("ercXXX/packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXX//packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXXXpackages.zeppelinos.eth/erc20?version=1.0.0"),
("erc1319packageszeppelinosetherc20version1.0.0"),
("erc1319:packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319:packages.zeppelinos.eth:1/erc20?version=1.0.0/"),
("erc1319:/packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319:/packages.zeppelinos.eth:1/erc20?version=1.0.0/"),
("erc1319/packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319//packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("erc1319packages.zeppelinos.eth:1/erc20?version=1.0.0"),
# wrong scheme
("http://packages.zeppelinos.eth/erc20?version=1.0.0"),
("ercXX://packages.zeppelinos.eth/erc20?version=1.0.0"),
("http://packages.zeppelinos.eth:1/erc20?version=1.0.0"),
("ercXX://packages.zeppelinos.eth:1/erc20?version=1.0.0"),
# no path
("ercXXX://"),
("erc1319://"),
# weird values
(b"ercXXX://zeppelinos.eth/erc20?version=1.0.0"),
(b"erc1319://zeppelinos.eth:1/erc20?version=1.0.0"),
("1234"),
({}),
),
Expand Down
21 changes: 21 additions & 0 deletions tests/ethpm/backends/test_registry_backend.py
@@ -0,0 +1,21 @@
from ethpm.backends.registry import RegistryURIBackend
from ethpm.exceptions import CannotHandleURI
import pytest


@pytest.fixture
def backend():
return RegistryURIBackend()


def test_registry_uri_backend(backend):
valid_uri = "erc1319://snakecharmers.eth:1/owned?version=1.0.0"
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) == 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW'


def test_registry_uri_backend_raises_exception_for_non_mainnet_chains(backend):
ropsten_uri = "erc1319://snakecharmers.eth:3/owned?version=1.0.0"
with pytest.raises(CannotHandleURI, match="Currently only mainnet"):
backend.fetch_uri_contents(ropsten_uri)
15 changes: 8 additions & 7 deletions tests/ethpm/test_uri.py
Expand Up @@ -68,17 +68,18 @@ def test_create_github_uri():
"uri,expected",
(
(
"ercXXX://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/owned?version=1.0.0",
["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "owned", "1.0.0"],
"erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned?version=1.0.0",
["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", "owned", "1.0.0"],
),
(
"ercXXX://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729/wallet?version=2.8.0/",
["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "wallet", "2.8.0"],
"erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet?version=2.8.0/",
["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", "wallet", "2.8.0"],
),
),
)
def test_parse_registry_uri(uri, expected):
address, pkg_name, pkg_version = parse_registry_uri(uri)
address, chain_id, pkg_name, pkg_version = parse_registry_uri(uri)
assert address == expected[0]
assert pkg_name == expected[1]
assert pkg_version == expected[2]
assert chain_id == expected[1]
assert pkg_name == expected[2]
assert pkg_version == expected[3]