Exemple #1
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param crytic_compile:
        :param target:
        :param kwargs:
        :return:
        """

        dapp_ignore_compile = kwargs.get("dapp_ignore_compile",
                                         False) or kwargs.get(
                                             "ignore_compile", False)
        directory = os.path.join(self._target, "out")

        if not dapp_ignore_compile:
            _run_dapp(self._target)

        crytic_compile.compiler_version = _get_version(self._target)

        files = glob.glob(directory + "/**/*.sol.json", recursive=True)
        for file in files:
            with open(file, encoding="utf8") as file_desc:
                targets_json = json.load(file_desc)

            if not "contracts" in targets_json:
                continue

            for original_contract_name, info in targets_json[
                    "contracts"].items():
                contract_name = extract_name(original_contract_name)
                contract_filename = extract_filename(original_contract_name)
                contract_filename = convert_filename(contract_filename,
                                                     _relative_to_short,
                                                     crytic_compile,
                                                     working_dir=self._target)
                crytic_compile.contracts_names.add(contract_name)
                crytic_compile.contracts_filenames[
                    contract_name] = contract_filename
                crytic_compile.abis[contract_name] = json.loads(info["abi"])
                crytic_compile.bytecodes_init[contract_name] = info["bin"]
                crytic_compile.bytecodes_runtime[contract_name] = info[
                    "bin-runtime"]
                crytic_compile.srcmaps_init[contract_name] = info[
                    "srcmap"].split(";")
                crytic_compile.srcmaps_runtime[contract_name] = info[
                    "srcmap-runtime"].split(";")

                userdoc = json.loads(info.get("userdoc", "{}"))
                devdoc = json.loads(info.get("devdoc", "{}"))
                natspec = Natspec(userdoc, devdoc)
                crytic_compile.natspec[contract_name] = natspec

            for path, info in targets_json["sources"].items():
                path = convert_filename(path,
                                        _relative_to_short,
                                        crytic_compile,
                                        working_dir=self._target)
                crytic_compile.filenames.add(path)
                crytic_compile.asts[path.absolute] = info["AST"]
Exemple #2
0
def solc_handle_contracts(
    targets_json: Dict,
    skip_filename: bool,
    compilation_unit: "CompilationUnit",
    target: str,
    solc_working_dir: Optional[str],
) -> None:
    """Populate the compilation unit from the compilation json artifacts

    Args:
        targets_json (Dict): Compilation artifacts
        skip_filename (bool): If true, skip the filename (for solc <0.4.10)
        compilation_unit (CompilationUnit): Associated compilation unit
        target (str): Path to the target
        solc_working_dir (Optional[str]): Working directory for running solc
    """
    is_above_0_8 = _is_at_or_above_minor_version(compilation_unit, 8)

    if "contracts" in targets_json:

        for original_contract_name, info in targets_json["contracts"].items():
            contract_name = extract_name(original_contract_name)
            # for solc < 0.4.10 we cant retrieve the filename from the ast
            if skip_filename:
                contract_filename = convert_filename(
                    target,
                    relative_to_short,
                    compilation_unit.crytic_compile,
                    working_dir=solc_working_dir,
                )
            else:
                contract_filename = convert_filename(
                    extract_filename(original_contract_name),
                    relative_to_short,
                    compilation_unit.crytic_compile,
                    working_dir=solc_working_dir,
                )
            compilation_unit.contracts_names.add(contract_name)
            compilation_unit.filename_to_contracts[contract_filename].add(
                contract_name)
            compilation_unit.abis[contract_name] = (json.loads(
                info["abi"]) if not is_above_0_8 else info["abi"])
            compilation_unit.bytecodes_init[contract_name] = info["bin"]
            compilation_unit.bytecodes_runtime[contract_name] = info[
                "bin-runtime"]
            compilation_unit.srcmaps_init[contract_name] = info[
                "srcmap"].split(";")
            compilation_unit.srcmaps_runtime[contract_name] = info[
                "srcmap-runtime"].split(";")
            userdoc = json.loads(info.get(
                "userdoc", "{}")) if not is_above_0_8 else info["userdoc"]
            devdoc = json.loads(info.get(
                "devdoc", "{}")) if not is_above_0_8 else info["devdoc"]
            natspec = Natspec(userdoc, devdoc)
            compilation_unit.natspec[contract_name] = natspec
Exemple #3
0
def solc_handle_contracts(
    targets_json: Dict,
    skip_filename: bool,
    compilation_unit: "CompilationUnit",
    target: str,
    solc_working_dir: Optional[str],
):
    is_above_0_8 = _is_at_or_above_minor_version(compilation_unit, 8)

    if "contracts" in targets_json:

        for original_contract_name, info in targets_json["contracts"].items():
            contract_name = extract_name(original_contract_name)
            contract_filename = extract_filename(original_contract_name)
            # for solc < 0.4.10 we cant retrieve the filename from the ast
            if skip_filename:
                contract_filename = convert_filename(
                    target,
                    relative_to_short,
                    compilation_unit.crytic_compile,
                    working_dir=solc_working_dir,
                )
            else:
                contract_filename = convert_filename(
                    contract_filename,
                    relative_to_short,
                    compilation_unit.crytic_compile,
                    working_dir=solc_working_dir,
                )
            compilation_unit.contracts_names.add(contract_name)
            compilation_unit.contracts_filenames[
                contract_name] = contract_filename
            compilation_unit.abis[contract_name] = (json.loads(
                info["abi"]) if not is_above_0_8 else info["abi"])
            compilation_unit.bytecodes_init[contract_name] = info["bin"]
            compilation_unit.bytecodes_runtime[contract_name] = info[
                "bin-runtime"]
            compilation_unit.srcmaps_init[contract_name] = info[
                "srcmap"].split(";")
            compilation_unit.srcmaps_runtime[contract_name] = info[
                "srcmap-runtime"].split(";")
            userdoc = json.loads(info.get(
                "userdoc", "{}")) if not is_above_0_8 else info["userdoc"]
            devdoc = json.loads(info.get(
                "devdoc", "{}")) if not is_above_0_8 else info["devdoc"]
            natspec = Natspec(userdoc, devdoc)
            compilation_unit.natspec[contract_name] = natspec
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param kwargs:
        :return:
        """

        hardhat_ignore_compile = kwargs.get("hardhat_ignore_compile",
                                            False) or kwargs.get(
                                                "ignore_compile", False)

        cache_directory = kwargs.get("hardhat_cache_directory", "cache")
        config_file = Path(cache_directory, "solidity-files-cache.json")

        build_directory = Path(
            kwargs.get("hardhat_cache_directory", "artifacts/build-info"))

        hardhat_working_dir = kwargs.get("hardhat_working_dir", None)

        base_cmd = ["hardhat"]
        if not kwargs.get("npx_disable", False):
            base_cmd = ["npx"] + base_cmd

        if not hardhat_ignore_compile:
            cmd = base_cmd + ["compile"]

            LOGGER.info(
                "'%s' running",
                " ".join(cmd),
            )

            process = subprocess.Popen(cmd,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       cwd=self._target)

            stdout_bytes, stderr_bytes = process.communicate()
            stdout, stderr = (
                stdout_bytes.decode(),
                stderr_bytes.decode(),
            )  # convert bytestrings to unicode strings

            LOGGER.info(stdout)
            if stderr:
                LOGGER.error(stderr)

        (compiler, version_from_config,
         optimized) = _get_version_from_config(config_file)

        crytic_compile.compiler_version = CompilerVersion(
            compiler=compiler,
            version=version_from_config,
            optimized=optimized)

        skip_filename = crytic_compile.compiler_version.version in [
            f"0.4.{x}" for x in range(0, 10)
        ]

        files = sorted(
            os.listdir(build_directory),
            key=lambda x: os.path.getmtime(Path(build_directory, x)))
        if not files:
            txt = f"`hardhat compile` failed. Can you run it?\n{build_directory} is empty"
            raise InvalidCompilation(txt)

        build_info = Path(build_directory, files[0])
        with open(build_info, encoding="utf8") as file_desc:
            targets_json = json.load(file_desc)["output"]

            if "contracts" in targets_json:
                for original_filename, contracts_info in targets_json[
                        "contracts"].items():
                    for original_contract_name, info in contracts_info.items():
                        contract_name = extract_name(original_contract_name)

                        contract_filename = convert_filename(
                            original_filename,
                            relative_to_short,
                            crytic_compile,
                            working_dir=hardhat_working_dir,
                        )

                        crytic_compile.contracts_names.add(contract_name)
                        crytic_compile.contracts_filenames[
                            contract_name] = contract_filename

                        crytic_compile.abis[contract_name] = info["abi"]
                        crytic_compile.bytecodes_init[contract_name] = info[
                            "evm"]["bytecode"]["object"]
                        crytic_compile.bytecodes_runtime[contract_name] = info[
                            "evm"]["deployedBytecode"]["object"]
                        crytic_compile.srcmaps_init[contract_name] = info[
                            "evm"]["bytecode"]["sourceMap"].split(";")
                        crytic_compile.srcmaps_runtime[contract_name] = info[
                            "evm"]["bytecode"]["sourceMap"].split(";")
                        userdoc = info.get("userdoc", {})
                        devdoc = info.get("devdoc", {})
                        natspec = Natspec(userdoc, devdoc)
                        crytic_compile.natspec[contract_name] = natspec

            if "sources" in targets_json:
                for path, info in targets_json["sources"].items():
                    if skip_filename:
                        path = convert_filename(
                            self._target,
                            relative_to_short,
                            crytic_compile,
                            working_dir=hardhat_working_dir,
                        )
                    else:
                        path = convert_filename(
                            path,
                            relative_to_short,
                            crytic_compile,
                            working_dir=hardhat_working_dir)
                    crytic_compile.filenames.add(path)
                    crytic_compile.asts[path.absolute] = info["ast"]
Exemple #5
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param crytic_compile:
        :param target:
        :param kwargs:
        :return:
        """

        dapp_ignore_compile = kwargs.get("dapp_ignore_compile",
                                         False) or kwargs.get(
                                             "ignore_compile", False)
        directory = os.path.join(self._target, "out")

        if not dapp_ignore_compile:
            _run_dapp(self._target)

        crytic_compile.compiler_version = _get_version(self._target)

        optimized = False

        with open(os.path.join(directory, "dapp.sol.json")) as file_desc:
            targets_json = json.load(file_desc)

            version = None
            if "version" in targets_json:
                version = re.findall(r"\d+\.\d+\.\d+",
                                     targets_json["version"])[0]

            for original_filename, contracts_info in targets_json[
                    "contracts"].items():
                for original_contract_name, info in contracts_info.items():
                    if "metadata" in info:
                        metadata = json.loads(info["metadata"])
                        if ("settings" in metadata
                                and "optimizer" in metadata["settings"]
                                and "enabled"
                                in metadata["settings"]["optimizer"]):
                            optimized |= metadata["settings"]["optimizer"][
                                "enabled"]
                    contract_name = extract_name(original_contract_name)
                    crytic_compile.contracts_names.add(contract_name)
                    crytic_compile.contracts_filenames[
                        contract_name] = original_filename

                    crytic_compile.abis[contract_name] = info["abi"]
                    crytic_compile.bytecodes_init[contract_name] = info["evm"][
                        "bytecode"]["object"]
                    crytic_compile.bytecodes_runtime[contract_name] = info[
                        "evm"]["deployedBytecode"]["object"]
                    crytic_compile.srcmaps_init[contract_name] = info["evm"][
                        "bytecode"]["sourceMap"].split(";")
                    crytic_compile.srcmaps_runtime[contract_name] = info[
                        "evm"]["bytecode"]["sourceMap"].split(";")
                    userdoc = info.get("userdoc", {})
                    devdoc = info.get("devdoc", {})
                    natspec = Natspec(userdoc, devdoc)
                    crytic_compile.natspec[contract_name] = natspec

                    if version is None:
                        metadata = json.loads(info["metadata"])
                        version = re.findall(
                            r"\d+\.\d+\.\d+",
                            metadata["compiler"]["version"])[0]

            for path, info in targets_json["sources"].items():
                path = convert_filename(path,
                                        _relative_to_short,
                                        crytic_compile,
                                        working_dir=self._target)
                crytic_compile.filenames.add(path)
                crytic_compile.asts[path.absolute] = info["ast"]

        crytic_compile.compiler_version = CompilerVersion(compiler="solc",
                                                          version=version,
                                                          optimized=optimized)
Exemple #6
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param kwargs:
        :return:
        """

        cache_directory = kwargs.get("buidler_cache_directory", "")
        target_solc_file = os.path.join(cache_directory, "solc-output.json")
        target_vyper_file = os.path.join(cache_directory,
                                         "vyper-docker-updates.json")
        buidler_ignore_compile = kwargs.get("buidler_ignore_compile",
                                            False) or kwargs.get(
                                                "buidler_compile", False)
        buidler_working_dir = kwargs.get("buidler_working_dir", None)

        base_cmd = ["buidler"]
        if not kwargs.get("npx_disable", False):
            base_cmd = ["npx"] + base_cmd

        if not buidler_ignore_compile:
            cmd = base_cmd + ["compile"]

            LOGGER.info(
                "'%s' running",
                " ".join(cmd),
            )

            process = subprocess.Popen(cmd,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       cwd=self._target)

            stdout_bytes, stderr_bytes = process.communicate()
            stdout, stderr = (
                stdout_bytes.decode(),
                stderr_bytes.decode(),
            )  # convert bytestrings to unicode strings

            LOGGER.info(stdout)
            if stderr:
                LOGGER.error(stderr)

        if not os.path.isfile(os.path.join(self._target, target_solc_file)):
            if os.path.isfile(os.path.join(self._target, target_vyper_file)):
                txt = 'Vyper not yet supported with buidler.'
                txt += ' Please open an issue in https://github.com/crytic/crytic-compile'
                raise InvalidCompilation(txt)
            txt = f"`buidler compile` failed. Can you run it?\n{os.path.join(self._target, target_solc_file)} not found"
            raise InvalidCompilation(txt)

        (compiler, version_from_config,
         optimized) = _get_version_from_config(cache_directory)

        crytic_compile.compiler_version = CompilerVersion(
            compiler=compiler,
            version=version_from_config,
            optimized=optimized)

        skip_filename = crytic_compile.compiler_version.version in [
            f"0.4.{x}" for x in range(0, 10)
        ]

        with open(target_solc_file, encoding="utf8") as file_desc:
            targets_json = json.load(file_desc)

            if "contracts" in targets_json:
                for original_filename, contracts_info in targets_json[
                        "contracts"].items():
                    for original_contract_name, info in contracts_info.items():
                        contract_name = extract_name(original_contract_name)

                        contract_filename = convert_filename(
                            original_filename,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir,
                        )

                        crytic_compile.contracts_names.add(contract_name)
                        crytic_compile.contracts_filenames[
                            contract_name] = contract_filename

                        crytic_compile.abis[contract_name] = info["abi"]
                        crytic_compile.bytecodes_init[contract_name] = info[
                            "evm"]["bytecode"]["object"]
                        crytic_compile.bytecodes_runtime[contract_name] = info[
                            "evm"]["deployedBytecode"]["object"]
                        crytic_compile.srcmaps_init[contract_name] = info[
                            "evm"]["bytecode"]["sourceMap"].split(";")
                        crytic_compile.srcmaps_runtime[contract_name] = info[
                            "evm"]["bytecode"]["sourceMap"].split(";")
                        userdoc = json.loads(info.get("userdoc", "{}"))
                        devdoc = json.loads(info.get("devdoc", "{}"))
                        natspec = Natspec(userdoc, devdoc)
                        crytic_compile.natspec[contract_name] = natspec

            if "sources" in targets_json:
                for path, info in targets_json["sources"].items():
                    if skip_filename:
                        path = convert_filename(
                            self._target,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir,
                        )
                    else:
                        path = convert_filename(
                            path,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir)
                    crytic_compile.filenames.add(path)
                    crytic_compile.asts[path.absolute] = info["ast"]
Exemple #7
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """

        Compile the tharget
        :param crytic_compile:
        :param target:
        :param kwargs:
        :return:
        """

        target = self._target

        solc = kwargs.get("solc", "solc")

        if target.startswith(tuple(SUPPORTED_NETWORK)):
            prefix: Union[None,
                          str] = SUPPORTED_NETWORK[target[:target.find(":") +
                                                          1]][0]
            prefix_bytecode = SUPPORTED_NETWORK[target[:target.find(":") +
                                                       1]][1]
            addr = target[target.find(":") + 1:]
            etherscan_url = ETHERSCAN_BASE % (prefix, addr)
            etherscan_bytecode_url = ETHERSCAN_BASE_BYTECODE % (
                prefix_bytecode, addr)

        else:
            etherscan_url = ETHERSCAN_BASE % ("", target)
            etherscan_bytecode_url = ETHERSCAN_BASE_BYTECODE % ("", target)
            addr = target
            prefix = None

        only_source = kwargs.get("etherscan_only_source_code", False)
        only_bytecode = kwargs.get("etherscan_only_bytecode", False)

        etherscan_api_key = kwargs.get("etherscan_api_key", None)

        if etherscan_api_key:
            etherscan_url += f"&apikey={etherscan_api_key}"
            etherscan_bytecode_url += f"&apikey={etherscan_api_key}"

        source_code: str = ""
        result: Dict[str, Union[bool, str, int]] = dict()
        contract_name: str = ""

        if not only_bytecode:
            with urllib.request.urlopen(etherscan_url) as response:
                html = response.read()

            info = json.loads(html)

            if "message" not in info:
                LOGGER.error("Incorrect etherscan request")
                raise InvalidCompilation("Incorrect etherscan request " +
                                         etherscan_url)

            if not info["message"].startswith("OK"):
                LOGGER.error("Contract has no public source code")
                raise InvalidCompilation(
                    "Contract has no public source code: " + etherscan_url)

            if "result" not in info:
                LOGGER.error("Contract has no public source code")
                raise InvalidCompilation(
                    "Contract has no public source code: " + etherscan_url)

            result = info["result"][0]
            # Assert to help mypy
            assert isinstance(result["SourceCode"], str)
            assert isinstance(result["ContractName"], str)

            source_code = result["SourceCode"]
            contract_name = result["ContractName"]

        if source_code == "" and not only_source:
            LOGGER.info(
                "Source code not available, try to fetch the bytecode only")

            req = urllib.request.Request(etherscan_bytecode_url,
                                         headers={"User-Agent": "Mozilla/5.0"})
            with urllib.request.urlopen(req) as response:
                html = response.read()

            _handle_bytecode(crytic_compile, target, html)
            return

        if source_code == "":
            LOGGER.error("Contract has no public source code")
            raise InvalidCompilation("Contract has no public source code: " +
                                     etherscan_url)

        if prefix:
            filename = os.path.join("crytic-export", "etherscan_contracts",
                                    f"{addr}{prefix}-{contract_name}.sol")
        else:
            filename = os.path.join("crytic-export", "etherscan_contracts",
                                    f"{addr}-{contract_name}.sol")

        if not os.path.exists("crytic-export"):
            os.makedirs("crytic-export")

        if not os.path.exists(
                os.path.join("crytic-export", "etherscan_contracts")):
            os.makedirs(os.path.join("crytic-export", "etherscan_contracts"))

        with open(filename, "w", encoding="utf8") as file_desc:
            file_desc.write(source_code)

        # Assert to help mypy
        assert isinstance(result["CompilerVersion"], str)

        compiler_version = re.findall(
            r"\d+\.\d+\.\d+", convert_version(result["CompilerVersion"]))[0]
        optimization_used: bool = result["OptimizationUsed"] == "1"

        solc_arguments = None
        if optimization_used:
            optimized_run = int(result["Runs"])
            solc_arguments = f"--optimize --optimize-runs {optimized_run}"

        crytic_compile.compiler_version = CompilerVersion(
            compiler="solc",
            version=compiler_version,
            optimized=optimization_used)

        targets_json = _run_solc(
            crytic_compile,
            filename,
            solc=solc,
            solc_disable_warnings=False,
            solc_arguments=solc_arguments,
            env=dict(os.environ, SOLC_VERSION=compiler_version),
        )

        for original_contract_name, info in targets_json["contracts"].items():
            contract_name = extract_name(original_contract_name)
            contract_filename = extract_filename(original_contract_name)
            contract_filename = convert_filename(contract_filename,
                                                 _relative_to_short,
                                                 crytic_compile)
            crytic_compile.contracts_names.add(contract_name)
            crytic_compile.contracts_filenames[
                contract_name] = contract_filename
            crytic_compile.abis[contract_name] = json.loads(info["abi"])
            crytic_compile.bytecodes_init[contract_name] = info["bin"]
            crytic_compile.bytecodes_runtime[contract_name] = info[
                "bin-runtime"]
            crytic_compile.srcmaps_init[contract_name] = info["srcmap"].split(
                ";")
            crytic_compile.srcmaps_runtime[contract_name] = info[
                "srcmap-runtime"].split(";")

            userdoc = json.loads(info.get("userdoc", "{}"))
            devdoc = json.loads(info.get("devdoc", "{}"))
            natspec = Natspec(userdoc, devdoc)
            crytic_compile.natspec[contract_name] = natspec

        for path, info in targets_json["sources"].items():
            path = convert_filename(path, _relative_to_short, crytic_compile)
            crytic_compile.filenames.add(path)
            crytic_compile.asts[path.absolute] = info["AST"]
Exemple #8
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
        """Run the compilation

        Args:
            crytic_compile (CryticCompile): Associated CryticCompile object
            **kwargs: optional arguments. Used: "hardhat_ignore", "hardhat_ignore_compile", "ignore_compile",
                "hardhat_artifacts_directory","hardhat_working_dir","npx_disable"

        Raises:
            InvalidCompilation: If hardhat failed to run
        """

        hardhat_ignore_compile = kwargs.get("hardhat_ignore_compile", False) or kwargs.get(
            "ignore_compile", False
        )

        build_directory = Path(
            self._target, kwargs.get("hardhat_artifacts_directory", "artifacts"), "build-info"
        )

        hardhat_working_dir = kwargs.get("hardhat_working_dir", self._target)

        base_cmd = ["hardhat"]
        if not kwargs.get("npx_disable", False):
            base_cmd = ["npx"] + base_cmd

        if not hardhat_ignore_compile:
            cmd = base_cmd + ["compile", "--force"]

            LOGGER.info(
                "'%s' running",
                " ".join(cmd),
            )

            with subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                cwd=self._target,
                executable=shutil.which(cmd[0]),
            ) as process:

                stdout_bytes, stderr_bytes = process.communicate()
                stdout, stderr = (
                    stdout_bytes.decode(),
                    stderr_bytes.decode(),
                )  # convert bytestrings to unicode strings

                LOGGER.info(stdout)
                if stderr:
                    LOGGER.error(stderr)

        files = sorted(
            os.listdir(build_directory), key=lambda x: os.path.getmtime(Path(build_directory, x))
        )
        files = [f for f in files if f.endswith(".json")]
        if not files:
            txt = f"`hardhat compile` failed. Can you run it?\n{build_directory} is empty"
            raise InvalidCompilation(txt)

        for file in files:
            build_info = Path(build_directory, file)

            # The file here should always ends .json, but just in case use ife
            uniq_id = file if ".json" not in file else file[0:-5]
            compilation_unit = CompilationUnit(crytic_compile, uniq_id)

            with open(build_info, encoding="utf8") as file_desc:
                loaded_json = json.load(file_desc)

                targets_json = loaded_json["output"]

                version_from_config = loaded_json["solcVersion"]  # TODO supper vyper
                input_json = loaded_json["input"]
                compiler = "solc" if input_json["language"] == "Solidity" else "vyper"
                optimized = input_json["settings"]["optimizer"]["enabled"]

                compilation_unit.compiler_version = CompilerVersion(
                    compiler=compiler, version=version_from_config, optimized=optimized
                )

                skip_filename = compilation_unit.compiler_version.version in [
                    f"0.4.{x}" for x in range(0, 10)
                ]

                if "contracts" in targets_json:
                    for original_filename, contracts_info in targets_json["contracts"].items():
                        for original_contract_name, info in contracts_info.items():
                            contract_name = extract_name(original_contract_name)

                            contract_filename = convert_filename(
                                original_filename,
                                relative_to_short,
                                crytic_compile,
                                working_dir=hardhat_working_dir,
                            )

                            compilation_unit.contracts_names.add(contract_name)
                            compilation_unit.filename_to_contracts[contract_filename].add(
                                contract_name
                            )

                            compilation_unit.abis[contract_name] = info["abi"]
                            compilation_unit.bytecodes_init[contract_name] = info["evm"][
                                "bytecode"
                            ]["object"]
                            compilation_unit.bytecodes_runtime[contract_name] = info["evm"][
                                "deployedBytecode"
                            ]["object"]
                            compilation_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][
                                "sourceMap"
                            ].split(";")
                            compilation_unit.srcmaps_runtime[contract_name] = info["evm"][
                                "deployedBytecode"
                            ]["sourceMap"].split(";")
                            userdoc = info.get("userdoc", {})
                            devdoc = info.get("devdoc", {})
                            natspec = Natspec(userdoc, devdoc)
                            compilation_unit.natspec[contract_name] = natspec

                if "sources" in targets_json:
                    for path, info in targets_json["sources"].items():
                        if skip_filename:
                            path = convert_filename(
                                self._target,
                                relative_to_short,
                                crytic_compile,
                                working_dir=hardhat_working_dir,
                            )
                        else:
                            path = convert_filename(
                                path,
                                relative_to_short,
                                crytic_compile,
                                working_dir=hardhat_working_dir,
                            )
                        crytic_compile.filenames.add(path)
                        compilation_unit.filenames.add(path)
                        compilation_unit.asts[path.absolute] = info["ast"]
Exemple #9
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param crytic_compile:
        :param target:
        :param kwargs:
        :return:
        """
        embark_ignore_compile = kwargs.get("embark_ignore_compile", False) or kwargs.get(
            "ignore_compile", False
        )
        embark_overwrite_config = kwargs.get("embark_overwrite_config", False)

        plugin_name = "@trailofbits/embark-contract-info"
        with open(os.path.join(self._target, "embark.json"), encoding="utf8") as file_desc:
            embark_json = json.load(file_desc)
        if embark_overwrite_config:
            write_embark_json = False
            if not "plugins" in embark_json:
                embark_json["plugins"] = {plugin_name: {"flags": ""}}
                write_embark_json = True
            elif not plugin_name in embark_json["plugins"]:
                embark_json["plugins"][plugin_name] = {"flags": ""}
                write_embark_json = True
            if write_embark_json:
                try:
                    process = subprocess.Popen(["npm", "install", plugin_name], cwd=self._target)
                except OSError as error:
                    raise InvalidCompilation(error)
                _, stderr = process.communicate()
                with open(
                        os.path.join(self._target, "embark.json"), "w", encoding="utf8"
                ) as outfile:
                    json.dump(embark_json, outfile, indent=2)
        else:
            if (not "plugins" in embark_json) or (not plugin_name in embark_json["plugins"]):
                raise InvalidCompilation(
                    "embark-contract-info plugin was found in embark.json. "
                    "Please install the plugin (see "
                    "https://github.com/crytic/crytic-compile/wiki/Usage#embark)"
                    ", or use --embark-overwrite-config."
                )

        if not embark_ignore_compile:
            try:
                cmd = ["embark", "build", "--contracts"]
                if not kwargs.get("npx_disable", False):
                    cmd = ["npx"] + cmd
                process = subprocess.Popen(cmd,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE,
                                           cwd=self._target)
            except OSError as error:
                raise InvalidCompilation(error)
            stdout, stderr = process.communicate()
            LOGGER.info("%s\n", stdout.decode())
            if stderr:
                # Embark might return information to stderr, but compile without issue
                LOGGER.error("%s", stderr.decode())
        infile = os.path.join(self._target, "crytic-export", "contracts-embark.json")
        if not os.path.isfile(infile):
            raise InvalidCompilation(
                "Embark did not generate the AST file. Is Embark installed "
                "(npm install -g embark)? Is embark-contract-info installed? (npm install -g embark)."
            )

        crytic_compile.compiler_version = _get_version(self._target)

        with open(infile, "r", encoding="utf8") as file_desc:
            targets_loaded = json.load(file_desc)
            for k, ast in targets_loaded["asts"].items():
                filename = convert_filename(
                    k, _relative_to_short, crytic_compile, working_dir=self._target
                )
                crytic_compile.asts[filename.absolute] = ast
                crytic_compile.filenames.add(filename)

            if not "contracts" in targets_loaded:
                LOGGER.error(
                    "Incorrect json file generated. Are you using %s >= 1.1.0?", plugin_name
                )
                raise InvalidCompilation(
                    f"Incorrect json file generated. Are you using {plugin_name} >= 1.1.0?"
                )

            for original_contract_name, info in targets_loaded["contracts"].items():
                contract_name = extract_name(original_contract_name)
                contract_filename = extract_filename(original_contract_name)
                contract_filename = convert_filename(
                    contract_filename, _relative_to_short, crytic_compile, working_dir=self._target
                )

                crytic_compile.contracts_filenames[contract_name] = contract_filename
                crytic_compile.contracts_names.add(contract_name)

                if "abi" in info:
                    crytic_compile.abis[contract_name] = info["abi"]
                if "bin" in info:
                    crytic_compile.bytecodes_init[contract_name] = info["bin"].replace("0x", "")
                if "bin-runtime" in info:
                    crytic_compile.bytecodes_runtime[contract_name] = info["bin-runtime"].replace(
                        "0x", ""
                    )
                if "srcmap" in info:
                    crytic_compile.srcmaps_init[contract_name] = info["srcmap"].split(";")
                if "srcmap-runtime" in info:
                    crytic_compile.srcmaps_runtime[contract_name] = info["srcmap-runtime"].split(
                        ";"
                    )

                userdoc = info.get("userdoc", {})
                devdoc = info.get("devdoc", {})
                natspec = Natspec(userdoc, devdoc)
                crytic_compile.natspec[contract_name] = natspec
Exemple #10
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str):
        """
        Compile the target

        :param crytic_compile:
        :param kwargs:
        :return:
        """

        solc = kwargs.get("solc", "solc")
        solc_disable_warnings = kwargs.get("solc_disable_warnings", False)
        solc_arguments = kwargs.get("solc_args", "")
        solc_remaps = kwargs.get("solc_remaps", None)
        solc_working_dir = kwargs.get("solc_working_dir", None)
        force_legacy_json = kwargs.get("solc_force_legacy_json", False)

        crytic_compile.compiler_version = CompilerVersion(
            compiler="solc", version=get_version(solc), optimized=is_optimized(solc_arguments)
        )

        # From config file, solcs is a dict (version -> path)
        # From command line, solc is a list
        # The guessing of version only works from config file
        # This is to prevent too complex command line
        solcs_path: Optional[Union[str, Dict, List[str]]] = kwargs.get("solc_solcs_bin")
        # solcs_env is always a list. It matches solc-select list
        solcs_env = kwargs.get("solc_solcs_select")

        if solcs_path:
            if isinstance(solcs_path, str):
                solcs_path = solcs_path.split(",")
            targets_json = _run_solcs_path(
                crytic_compile,
                self._target,
                solcs_path,
                solc_disable_warnings,
                solc_arguments,
                solc_remaps=solc_remaps,
                working_dir=solc_working_dir,
                force_legacy_json=force_legacy_json,
            )

        elif solcs_env:
            solcs_env_list = solcs_env.split(",")
            targets_json = _run_solcs_env(
                crytic_compile,
                self._target,
                solc,
                solc_disable_warnings,
                solc_arguments,
                solcs_env=solcs_env_list,
                solc_remaps=solc_remaps,
                working_dir=solc_working_dir,
                force_legacy_json=force_legacy_json,
            )

        else:
            targets_json = _run_solc(
                crytic_compile,
                self._target,
                solc,
                solc_disable_warnings,
                solc_arguments,
                solc_remaps=solc_remaps,
                working_dir=solc_working_dir,
                force_legacy_json=force_legacy_json,
            )

        skip_filename = crytic_compile.compiler_version.version in [
            f"0.4.{x}" for x in range(0, 10)
        ]

        if "contracts" in targets_json:
            for original_contract_name, info in targets_json["contracts"].items():
                contract_name = extract_name(original_contract_name)
                contract_filename = extract_filename(original_contract_name)
                # for solc < 0.4.10 we cant retrieve the filename from the ast
                if skip_filename:
                    contract_filename = convert_filename(
                        self._target,
                        relative_to_short,
                        crytic_compile,
                        working_dir=solc_working_dir,
                    )
                else:
                    contract_filename = convert_filename(
                        contract_filename,
                        relative_to_short,
                        crytic_compile,
                        working_dir=solc_working_dir,
                    )
                crytic_compile.contracts_names.add(contract_name)
                crytic_compile.contracts_filenames[contract_name] = contract_filename
                crytic_compile.abis[contract_name] = json.loads(info["abi"])
                crytic_compile.bytecodes_init[contract_name] = info["bin"]
                crytic_compile.bytecodes_runtime[contract_name] = info["bin-runtime"]
                crytic_compile.srcmaps_init[contract_name] = info["srcmap"].split(";")
                crytic_compile.srcmaps_runtime[contract_name] = info["srcmap-runtime"].split(";")
                userdoc = json.loads(info.get("userdoc", "{}"))
                devdoc = json.loads(info.get("devdoc", "{}"))
                natspec = Natspec(userdoc, devdoc)
                crytic_compile.natspec[contract_name] = natspec

        if "sources" in targets_json:
            for path, info in targets_json["sources"].items():
                if skip_filename:
                    path = convert_filename(
                        self._target,
                        relative_to_short,
                        crytic_compile,
                        working_dir=solc_working_dir,
                    )
                else:
                    path = convert_filename(
                        path, relative_to_short, crytic_compile, working_dir=solc_working_dir
                    )
                crytic_compile.filenames.add(path)
                crytic_compile.asts[path.absolute] = info["AST"]
Exemple #11
0
    def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
        """Run the compilation

        Args:
            crytic_compile (CryticCompile): Associated CryticCompile objects
            **kwargs: optional arguments. Used: "buidler_cache_directory", "buidler_ignore_compile", "ignore_compile",
                "buidler_working_dir", "buidler_skip_directory_name_fix", "npx_disable"

        Raises:
            InvalidCompilation: If buidler failed to run
        """

        cache_directory = kwargs.get("buidler_cache_directory", "")
        target_solc_file = os.path.join(cache_directory, "solc-output.json")
        target_vyper_file = os.path.join(cache_directory,
                                         "vyper-docker-updates.json")
        buidler_ignore_compile = kwargs.get("buidler_ignore_compile",
                                            False) or kwargs.get(
                                                "ignore_compile", False)
        buidler_working_dir = kwargs.get("buidler_working_dir", None)
        # See https://github.com/crytic/crytic-compile/issues/116
        skip_directory_name_fix = kwargs.get("buidler_skip_directory_name_fix",
                                             False)

        base_cmd = ["buidler"]
        if not kwargs.get("npx_disable", False):
            base_cmd = ["npx"] + base_cmd

        if not buidler_ignore_compile:
            cmd = base_cmd + ["compile"]

            LOGGER.info(
                "'%s' running",
                " ".join(cmd),
            )

            with subprocess.Popen(
                    cmd,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    cwd=self._target,
                    executable=shutil.which(cmd[0]),
            ) as process:

                stdout_bytes, stderr_bytes = process.communicate()
                stdout, stderr = (
                    stdout_bytes.decode(),
                    stderr_bytes.decode(),
                )  # convert bytestrings to unicode strings

                LOGGER.info(stdout)
                if stderr:
                    LOGGER.error(stderr)

        if not os.path.isfile(os.path.join(self._target, target_solc_file)):
            if os.path.isfile(os.path.join(self._target, target_vyper_file)):
                txt = "Vyper not yet supported with buidler."
                txt += " Please open an issue in https://github.com/crytic/crytic-compile"
                raise InvalidCompilation(txt)
            txt = f"`buidler compile` failed. Can you run it?\n{os.path.join(self._target, target_solc_file)} not found"
            raise InvalidCompilation(txt)

        compilation_unit = CompilationUnit(crytic_compile,
                                           str(target_solc_file))

        (compiler, version_from_config,
         optimized) = _get_version_from_config(Path(cache_directory))

        compilation_unit.compiler_version = CompilerVersion(
            compiler=compiler,
            version=version_from_config,
            optimized=optimized)

        skip_filename = compilation_unit.compiler_version.version in [
            f"0.4.{x}" for x in range(0, 10)
        ]

        with open(target_solc_file, encoding="utf8") as file_desc:
            targets_json = json.load(file_desc)

            if "contracts" in targets_json:
                for original_filename, contracts_info in targets_json[
                        "contracts"].items():
                    for original_contract_name, info in contracts_info.items():
                        contract_name = extract_name(original_contract_name)

                        if (original_filename.startswith("ontracts/")
                                and not skip_directory_name_fix):
                            original_filename = "c" + original_filename

                        contract_filename = convert_filename(
                            original_filename,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir,
                        )

                        compilation_unit.contracts_names.add(contract_name)
                        compilation_unit.filename_to_contracts[
                            contract_filename].add(contract_name)

                        compilation_unit.abis[contract_name] = info["abi"]
                        compilation_unit.bytecodes_init[contract_name] = info[
                            "evm"]["bytecode"]["object"]
                        compilation_unit.bytecodes_runtime[
                            contract_name] = info["evm"]["deployedBytecode"][
                                "object"]
                        compilation_unit.srcmaps_init[contract_name] = info[
                            "evm"]["bytecode"]["sourceMap"].split(";")
                        compilation_unit.srcmaps_runtime[contract_name] = info[
                            "evm"]["deployedBytecode"]["sourceMap"].split(";")
                        userdoc = info.get("userdoc", {})
                        devdoc = info.get("devdoc", {})
                        natspec = Natspec(userdoc, devdoc)
                        compilation_unit.natspec[contract_name] = natspec

            if "sources" in targets_json:
                for path, info in targets_json["sources"].items():

                    if path.startswith(
                            "ontracts/") and not skip_directory_name_fix:
                        path = "c" + path

                    if skip_filename:
                        path = convert_filename(
                            self._target,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir,
                        )
                    else:
                        path = convert_filename(
                            path,
                            relative_to_short,
                            crytic_compile,
                            working_dir=buidler_working_dir)
                    compilation_unit.filenames.add(path)
                    crytic_compile.filenames.add(path)
                    compilation_unit.asts[path.absolute] = info["ast"]