def _get_uri_contents(uri: str) -> str: path = _get_data_folder().joinpath( f"ipfs_cache/{urlparse(uri).netloc}.ipfs") path.parent.mkdir(exist_ok=True) if not path.exists(): data = InfuraIPFSBackend().fetch_uri_contents(uri) with path.open("wb") as fp: fp.write(data) return data.decode("utf-8") with path.open() as fp: data = fp.read() return data
def verify_manifest(package_name: str, version: str, uri: str) -> None: """ Verifies the validity of a package at a given IPFS URI. Arguments: package_name: Package name version: Package version uri: IPFS uri Returns None if the package is valid, raises InvalidManifest if not. """ _verify_package_name(package_name) data = InfuraIPFSBackend().fetch_uri_contents(uri).decode("utf-8") try: manifest = json.loads(data) except Exception: raise InvalidManifest("URI did not return valid JSON encoded data") if json.dumps(manifest, sort_keys=True, separators=(",", ":")) != data: raise InvalidManifest( "JSON data is not tightly packed with sorted keys") for key, value in [ ("manifest_version", "2"), ("package_name", package_name), ("version", version), ]: if manifest.get(key, None) != value: raise InvalidManifest(f"Missing or invalid field: {key}") try: process_manifest(manifest) except Exception as e: raise InvalidManifest(f"Cannot process manifest - {str(e)}")
def get_ipfs_backend(ipfs: bool = False) -> BaseIPFSBackend: if ipfs: return LocalIPFSBackend() return InfuraIPFSBackend()
def test_base_ipfs_gateway_backend_correctly_handles_uri_schemes(uri, expected): backend = InfuraIPFSBackend() assert backend.can_resolve_uri(uri) is expected
@pytest.fixture def fake_client(): class FakeClient: def cat(self, ipfs_hash): return ipfs_hash return FakeClient() @pytest.mark.parametrize( "base_uri,backend", ( (IPFS_GATEWAY_PREFIX, IPFSGatewayBackend()), (INFURA_GATEWAY_PREFIX, InfuraIPFSBackend()), ), ) def test_ipfs_and_infura_gateway_backends_fetch_uri_contents( base_uri, backend, safe_math_manifest ): uri = "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" assert backend.base_uri == base_uri with requests_mock.Mocker() as m: m.get(requests_mock.ANY, text=json.dumps(safe_math_manifest)) contents = backend.fetch_uri_contents(uri) contents_dict = json.loads(to_text(contents)) assert contents_dict["package_name"] == "safe-math-lib" def test_local_ipfs_backend(monkeypatch, fake_client):
def create_manifest(project_path: Path, package_config: Dict, pin_assets: bool = False, silent: bool = True) -> Tuple[Dict, str]: """ Creates a manifest from a project, and optionally pins it to IPFS. Arguments: project_path: Path to the root folder of the project package_config: Configuration settings for the manifest pin_assets: if True, all source files and the manifest will be uploaded onto IPFS via Infura. Returns: generated manifest, ipfs uri of manifest """ package_config = _remove_empty_fields(package_config) _verify_package_name(package_config["package_name"]) if pin_assets: ipfs_backend = InfuraIPFSBackend() manifest = { "manifest_version": "2", "package_name": package_config["package_name"], "version": package_config["version"], "sources": {}, "contract_types": {}, } if "meta" in package_config: manifest["meta"] = package_config["meta"] # load packages.json and add build_dependencies packages_json: Dict = {"sources": {}, "packages": {}} if not package_config["settings"]["include_dependencies"]: installed, modified = get_installed_packages(project_path) if modified: raise InvalidManifest( f"Dependencies have been modified locally: {', '.join([i[0] for i in modified])}" ) if installed: packages_json = _load_packages_json(project_path) manifest["build_dependencies"] = dict( (k, v["manifest_uri"]) for k, v in packages_json["packages"].items()) # add sources contract_path = project_path.joinpath("contracts") for path in contract_path.glob("**/*.sol"): if path.relative_to( project_path).as_posix() in packages_json["sources"]: continue if pin_assets: if not silent: print( f'Pinning "{color("bright magenta")}{path.name}{color}"...' ) uri = ipfs_backend.pin_assets(path)[0]["Hash"] else: with path.open("rb") as fp: uri = generate_file_hash(fp.read()) manifest["sources"][ f"./{path.relative_to(contract_path).as_posix()}"] = f"ipfs://{uri}" # add contract_types for path in project_path.glob("build/contracts/*.json"): with path.open() as fp: build_json = json.load(fp) if not build_json["bytecode"]: # skip contracts that cannot deploy continue if build_json["sourcePath"] in packages_json["sources"]: # skip dependencies continue manifest["contract_types"][ build_json["contractName"]] = _get_contract_type(build_json) # add deployments deployment_networks = package_config["settings"]["deployment_networks"] if deployment_networks: active_network = network.show_active() if active_network: network.disconnect() manifest["deployments"] = {} if isinstance(deployment_networks, str): deployment_networks = [deployment_networks] if deployment_networks == ["*"]: deployment_networks = [ i.stem for i in project_path.glob("build/deployments/*") ] for network_name in deployment_networks: instances = list( project_path.glob(f"build/deployments/{network_name}/*.json")) if not instances: continue instances.sort(key=lambda k: k.stat().st_mtime, reverse=True) network.connect(network_name) manifest["deployments"][web3.chain_uri] = {} for path in instances: with path.open() as fp: build_json = json.load(fp) alias = build_json["contractName"] source_path = build_json["sourcePath"] if source_path in packages_json["sources"]: alias = f"{packages_json['sources'][source_path]['packages'][0]}:{alias}" if alias in manifest["contract_types"]: # skip deployment if bytecode does not match that of contract_type bytecode = manifest["contract_types"][alias][ "deployment_bytecode"]["bytecode"] if f"0x{build_json['bytecode']}" != bytecode: continue else: # add contract_type for dependency manifest["contract_types"][alias] = _get_contract_type( build_json) key = build_json["contractName"] for i in itertools.count(1): if key not in manifest["deployments"][web3.chain_uri]: break key = f"{build_json['contractName']}-{i}" manifest["deployments"][web3.chain_uri][key] = { "address": path.stem, "contract_type": alias, } network.disconnect() if active_network: network.connect(active_network) if not manifest["deployments"]: del manifest["deployments"] uri = None if pin_assets: if not silent: print("Pinning manifest...") temp_path = Path(tempfile.gettempdir()).joinpath("manifest.json") with temp_path.open("w") as fp: json.dump(manifest, fp, sort_keys=True, separators=(",", ":")) uri = ipfs_backend.pin_assets(temp_path)[0]["Hash"] return manifest, uri
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") def test_local_ipfs_backend(): 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")
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") 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")