コード例 #1
0
ファイル: solidity.py プロジェクト: tangy-ong/brownie
def _get_unique_build_json(output_evm: Dict, contract_node: Any,
                           stmt_nodes: Dict, branch_nodes: Dict,
                           has_fallback: bool) -> Dict:
    paths = {
        str(i.contract_id): i.parent().absolutePath
        for i in [contract_node] + contract_node.dependencies
    }

    bytecode = _format_link_references(output_evm)
    without_metadata = _remove_metadata(
        output_evm["deployedBytecode"]["object"])
    instruction_count = len(without_metadata) // 2

    pc_map, statement_map, branch_map = _generate_coverage_data(
        output_evm["deployedBytecode"]["sourceMap"],
        output_evm["deployedBytecode"]["opcodes"],
        contract_node,
        stmt_nodes,
        branch_nodes,
        has_fallback,
        instruction_count,
    )

    dependencies = []
    for node in [
            i for i in contract_node.dependencies
            if i.nodeType == "ContractDefinition"
    ]:
        # use contract aliases when recording dependencies, to avoid
        # potential namespace collisions when importing across projects
        name = node.name
        path_str = node.parent().absolutePath
        dependencies.append(_get_alias(name, path_str))

    return {
        "allSourcePaths": paths,
        "bytecode": bytecode,
        "bytecodeSha1": sha1(_remove_metadata(bytecode).encode()).hexdigest(),
        "coverageMap": {
            "statements": statement_map,
            "branches": branch_map
        },
        "dependencies": dependencies,
        "offset": contract_node.offset,
        "pcMap": pc_map,
        "type": contract_node.contractKind,
    }
コード例 #2
0
def get_abi(
    contract_sources: Dict[str, str],
    allow_paths: Optional[str] = None,
    remappings: Optional[list] = None,
    silent: bool = True,
) -> Dict:
    """
    Generate ABIs from contract interfaces.

    Arguments
    ---------
    contract_sources : dict
        a dictionary in the form of {'path': "source code"}
    allow_paths : str, optional
        Compiler allowed filesystem import path
    remappings : list, optional
        List of solidity path remappings
    silent : bool, optional
        Disable verbose reporting

    Returns
    -------
    dict
        Compiled ABIs in the format `{'contractName': [ABI]}`
    """

    final_output = {
        Path(k).stem: {
            "abi": json.loads(v),
            "contractName": Path(k).stem,
            "type": "interface",
            "source": None,
            "offset": None,
            "sha1": sha1(v.encode()).hexdigest(),
        }
        for k, v in contract_sources.items() if Path(k).suffix == ".json"
    }

    for path, source in [(k, v) for k, v in contract_sources.items()
                         if Path(k).suffix == ".vy"]:
        input_json = generate_input_json({path: source}, language="Vyper")
        input_json["settings"]["outputSelection"]["*"] = {"*": ["abi"]}
        try:
            output_json = compile_from_input_json(input_json, silent,
                                                  allow_paths)
        except Exception:
            # vyper interfaces do not convert to ABIs
            # https://github.com/vyperlang/vyper/issues/1944
            continue
        name = Path(path).stem
        final_output[name] = {
            "abi": output_json["contracts"][path][name]["abi"],
            "contractName": name,
            "type": "interface",
            "source": source,
            "offset": [0, len(source)],
            "sha1": sha1(contract_sources[path].encode()).hexdigest(),
        }

    solc_sources = {
        k: v
        for k, v in contract_sources.items() if Path(k).suffix == ".sol"
    }

    if not solc_sources:
        return final_output

    compiler_targets = find_solc_versions(solc_sources,
                                          install_needed=True,
                                          silent=silent)

    for version, path_list in compiler_targets.items():
        to_compile = {
            k: v
            for k, v in contract_sources.items() if k in path_list
        }

        set_solc_version(version)
        input_json = generate_input_json(to_compile,
                                         language="Solidity",
                                         remappings=remappings)
        input_json["settings"]["outputSelection"]["*"] = {
            "*": ["abi"],
            "": ["ast"]
        }

        output_json = compile_from_input_json(input_json, silent, allow_paths)
        source_nodes = solcast.from_standard_output(output_json)
        abi_json = {
            k: v
            for k, v in output_json["contracts"].items() if k in path_list
        }

        for path, name, data in [(k, x, y) for k, v in abi_json.items()
                                 for x, y in v.items()]:
            contract_node = next(i[name] for i in source_nodes
                                 if i.absolutePath == path)
            dependencies = []
            for node in [
                    i for i in contract_node.dependencies
                    if i.nodeType == "ContractDefinition"
            ]:
                dependency_name = node.name
                path_str = node.parent().absolutePath
                dependencies.append(_get_alias(dependency_name, path_str))

            final_output[name] = {
                "abi": data["abi"],
                "ast": output_json["sources"][path]["ast"],
                "contractName": name,
                "dependencies": dependencies,
                "type": "interface",
                "source": contract_sources[path],
                "offset": contract_node.offset,
                "sha1": sha1(contract_sources[path].encode()).hexdigest(),
            }

    return final_output
コード例 #3
0
def generate_build_json(input_json: Dict,
                        output_json: Dict,
                        compiler_data: Optional[Dict] = None,
                        silent: bool = True) -> Dict:
    """Formats standard compiler output to the brownie build json.

    Args:
        input_json: solc input json used to compile
        output_json: output json returned by compiler
        compiler_data: additonal data to include under 'compiler' in build json
        silent: verbose reporting

    Returns: build json dict"""

    if input_json["language"] not in ("Solidity", "Vyper"):
        raise UnsupportedLanguage(f"{input_json['language']}")

    if not silent:
        print("Generating build data...")

    if compiler_data is None:
        compiler_data = {}
    compiler_data["evm_version"] = input_json["settings"]["evmVersion"]
    build_json: Dict = {}

    if input_json["language"] == "Solidity":
        compiler_data["optimizer"] = input_json["settings"]["optimizer"]
        source_nodes, statement_nodes, branch_nodes = solidity._get_nodes(
            output_json)

    for path_str, contract_name in [
        (k, x) for k, v in output_json["contracts"].items() for x in v.keys()
    ]:
        contract_alias = contract_name

        if path_str in input_json["sources"]:
            source = input_json["sources"][path_str]["content"]
        else:
            with Path(path_str).open() as fp:
                source = fp.read()
            contract_alias = _get_alias(contract_name, path_str)

        if not silent:
            print(f" - {contract_alias}")

        abi = output_json["contracts"][path_str][contract_name]["abi"]
        natspec = merge_natspec(
            output_json["contracts"][path_str][contract_name].get(
                "devdoc", {}),
            output_json["contracts"][path_str][contract_name].get(
                "userdoc", {}),
        )
        output_evm = output_json["contracts"][path_str][contract_name]["evm"]
        if contract_alias in build_json and not output_evm["deployedBytecode"][
                "object"]:
            continue

        if input_json["language"] == "Solidity":
            contract_node = next(i[contract_name] for i in source_nodes
                                 if i.absolutePath == path_str)
            build_json[contract_alias] = solidity._get_unique_build_json(
                output_evm,
                contract_node,
                statement_nodes,
                branch_nodes,
                next((True for i in abi if i["type"] == "fallback"), False),
            )

        else:
            if contract_name == "<stdin>":
                contract_name = contract_alias = "Vyper"
            build_json[contract_alias] = vyper._get_unique_build_json(
                output_evm,
                path_str,
                contract_alias,
                output_json["sources"][path_str]["ast"],
                (0, len(source)),
            )

        build_json[contract_alias].update({
            "abi":
            abi,
            "ast":
            output_json["sources"][path_str]["ast"],
            "compiler":
            compiler_data,
            "contractName":
            contract_name,
            "deployedBytecode":
            output_evm["deployedBytecode"]["object"],
            "deployedSourceMap":
            output_evm["deployedBytecode"]["sourceMap"],
            "language":
            input_json["language"],
            "natspec":
            natspec,
            "opcodes":
            output_evm["deployedBytecode"]["opcodes"],
            "sha1":
            sha1(source.encode()).hexdigest(),
            "source":
            source,
            "sourceMap":
            output_evm["bytecode"].get("sourceMap", ""),
            "sourcePath":
            path_str,
        })
        size = len(remove_0x_prefix(
            output_evm["deployedBytecode"]["object"])) / 2  # type: ignore
        if size > 24577:
            notify(
                "WARNING",
                f"deployed size of {contract_name} is {size} bytes, exceeds EIP-170 limit of 24577",
            )

    if not silent:
        print("")

    return build_json