Esempio n. 1
0
def get_interface_codes(root_path: Path, contract_sources: ContractCodes) -> Dict:
    interface_codes: Dict = {}
    interfaces: Dict = {}

    for file_path, code in contract_sources.items():
        interfaces[file_path] = {}
        parent_path = root_path.joinpath(file_path).parent

        interface_codes = extract_file_interface_imports(code)
        for interface_name, interface_path in interface_codes.items():

            base_paths = [parent_path]
            if not interface_path.startswith(".") and root_path.joinpath(file_path).exists():
                base_paths.append(root_path)
            elif interface_path.startswith("../") and len(Path(file_path).parent.parts) < Path(
                interface_path
            ).parts.count(".."):
                raise FileNotFoundError(
                    f"{file_path} - Cannot perform relative import outside of base folder"
                )

            valid_path = get_interface_file_path(base_paths, interface_path)
            with valid_path.open() as fh:
                code = fh.read()
                if valid_path.suffix == ".json":
                    interfaces[file_path][interface_name] = {
                        "type": "json",
                        "code": json.loads(code.encode()),
                    }
                else:
                    interfaces[file_path][interface_name] = {"type": "vyper", "code": code}

    return interfaces
Esempio n. 2
0
def get_interface_codes(
    root_path: Union[Path, None],
    contract_path: ContractPath,
    contract_sources: ContractCodes,
    interface_sources: Dict,
) -> Dict:
    interface_codes: Dict = {}
    interfaces: Dict = {}

    code = contract_sources[contract_path]
    interface_codes = extract_file_interface_imports(code)
    for interface_name, interface_path in interface_codes.items():
        path = Path(contract_path).parent.joinpath(interface_path).as_posix()
        keys = [_standardize_path(path)]
        if not interface_path.startswith("."):
            keys.append(interface_path)

        key = next((i for i in keys if i in interface_sources), None)
        if key:
            interfaces[interface_name] = interface_sources[key]
            continue

        key = next((i + ".vy" for i in keys if i + ".vy" in contract_sources),
                   None)
        if key:
            interfaces[interface_name] = {
                "type": "vyper",
                "code": contract_sources[key]
            }
            continue

        if root_path is None:
            raise FileNotFoundError(
                f"Cannot locate interface '{interface_path}{{.vy,.json}}'")

        parent_path = root_path.joinpath(contract_path).parent
        base_paths = [parent_path]
        if not interface_path.startswith("."):
            base_paths.append(root_path)
        elif interface_path.startswith("../") and len(
                Path(contract_path).parent.parts) < Path(
                    interface_path).parts.count(".."):
            raise FileNotFoundError(
                f"{contract_path} - Cannot perform relative import outside of base folder"
            )

        valid_path = get_interface_file_path(base_paths, interface_path)
        with valid_path.open() as fh:
            code = fh.read()
        if valid_path.suffix == ".json":
            interfaces[interface_name] = {
                "type": "json",
                "code": json.loads(code.encode())
            }
        else:
            interfaces[interface_name] = {"type": "vyper", "code": code}

    return interfaces
Esempio n. 3
0
def get_interface_codes(root_path: Path, contract_sources: ContractCodes) -> Dict:
    interface_codes: Dict = {}
    interfaces: Dict = {}

    for file_path, code in contract_sources.items():
        interfaces[file_path] = {}
        parent_path = root_path.joinpath(file_path).parent

        interface_codes = extract_file_interface_imports(code)
        for interface_name, interface_path in interface_codes.items():

            base_paths = [parent_path]
            if not interface_path.startswith(".") and root_path.joinpath(file_path).exists():
                base_paths.append(root_path)
            elif interface_path.startswith("../") and len(Path(file_path).parent.parts) < Path(
                interface_path
            ).parts.count(".."):
                raise FileNotFoundError(
                    f"{file_path} - Cannot perform relative import outside of base folder"
                )

            valid_path = get_interface_file_path(base_paths, interface_path)
            with valid_path.open() as fh:
                code = fh.read()
                if valid_path.suffix == ".json":
                    contents = json.loads(code.encode())

                    # EthPM Manifest (EIP-2678)
                    if "contractTypes" in contents:
                        if (
                            interface_name not in contents["contractTypes"]
                            or "abi" not in contents["contractTypes"][interface_name]
                        ):
                            raise ValueError(
                                f"Could not find interface '{interface_name}'"
                                f" in manifest '{valid_path}'."
                            )

                        interfaces[file_path][interface_name] = {
                            "type": "json",
                            "code": contents["contractTypes"][interface_name]["abi"],
                        }

                    # ABI JSON file (either `List[ABI]` or `{"abi": List[ABI]}`)
                    elif isinstance(contents, list) or (
                        "abi" in contents and isinstance(contents["abi"], list)
                    ):
                        interfaces[file_path][interface_name] = {"type": "json", "code": contents}

                    else:
                        raise ValueError(f"Corrupted file: '{valid_path}'")

                else:
                    interfaces[file_path][interface_name] = {"type": "vyper", "code": code}

    return interfaces
Esempio n. 4
0
def test_extract_file_interface_imports_raises(code, assert_compile_failed):
    assert_compile_failed(lambda: extract_file_interface_imports(code),
                          StructureException)
Esempio n. 5
0
def test_extract_file_interface_imports(code):

    assert extract_file_interface_imports(code[0]) == {"Foo": code[1]}
Esempio n. 6
0
def get_interface_codes(
    root_path: Union[Path, None],
    contract_path: ContractPath,
    contract_sources: ContractCodes,
    interface_sources: Dict,
) -> Dict:
    interface_codes: Dict = {}
    interfaces: Dict = {}

    code = contract_sources[contract_path]
    interface_codes = extract_file_interface_imports(code)
    for interface_name, interface_path in interface_codes.items():
        # If we know the interfaces already (e.g. EthPM Manifest file)
        if interface_name in interface_sources:
            interfaces[interface_name] = interface_sources[interface_name]
            continue

        path = Path(contract_path).parent.joinpath(interface_path).as_posix()
        keys = [_standardize_path(path)]
        if not interface_path.startswith("."):
            keys.append(interface_path)

        key = next((i for i in keys if i in interface_sources), None)
        if key:
            interfaces[interface_name] = interface_sources[key]
            continue

        key = next((i + ".vy" for i in keys if i + ".vy" in contract_sources),
                   None)
        if key:
            interfaces[interface_name] = {
                "type": "vyper",
                "code": contract_sources[key]
            }
            continue

        if root_path is None:
            raise FileNotFoundError(
                f"Cannot locate interface '{interface_path}{{.vy,.json}}'")

        parent_path = root_path.joinpath(contract_path).parent
        base_paths = [parent_path]
        if not interface_path.startswith("."):
            base_paths.append(root_path)
        elif interface_path.startswith("../") and len(
                Path(contract_path).parent.parts) < Path(
                    interface_path).parts.count(".."):
            raise FileNotFoundError(
                f"{contract_path} - Cannot perform relative import outside of base folder"
            )

        valid_path = get_interface_file_path(base_paths, interface_path)
        with valid_path.open() as fh:
            code = fh.read()
        if valid_path.suffix == ".json":
            code_dict = json.loads(code.encode())
            # EthPM Manifest v3 (EIP-2678)
            if "contractTypes" in code_dict:
                if interface_name not in code_dict["contractTypes"]:
                    raise JSONError(
                        f"'{interface_name}' not found in '{valid_path}'")

                if "abi" not in code_dict["contractTypes"][interface_name]:
                    raise JSONError(
                        f"Missing abi for '{interface_name}' in '{valid_path}'"
                    )

                abi = code_dict["contractTypes"][interface_name]["abi"]
                interfaces[interface_name] = {"type": "json", "code": abi}

            # ABI JSON (`{"abi": List[ABI]}`)
            elif "abi" in code_dict:
                interfaces[interface_name] = {
                    "type": "json",
                    "code": code_dict["abi"]
                }

            # ABI JSON (`List[ABI]`)
            elif isinstance(code_dict, list):
                interfaces[interface_name] = {
                    "type": "json",
                    "code": code_dict
                }

            else:
                raise JSONError(f"Unexpected type in file: '{valid_path}'")

        else:
            interfaces[interface_name] = {"type": "vyper", "code": code}

    return interfaces
Esempio n. 7
0
    def compile(self, filename):
        """ Compile a single source contract at :code:`filename`

        :param filename: Source contract's filename
        """
        log.info("Compiling contract {}".format(filename))

        # Get our ouput FS stuff ready
        source_file = Path(self.dir, filename)
        name, ext = get_filename_and_ext(filename)
        contract_outdir = Path(self.builddir, name)
        contract_outdir.mkdir(mode=0o755, exist_ok=True, parents=True)
        bin_outfile = contract_outdir.joinpath('{}.bin'.format(name))
        abi_outfile = contract_outdir.joinpath('{}.abi'.format(name))

        if ext == 'sol':

            if is_solidity_interface_only(source_file):
                log.warning("{} appears to be a Solidity interface.  Skipping.".format(name))
                return

            # Compiler command to run
            compile_cmd = [
                SOLC_PATH,
                '--bin',
                '--optimize',
                '--overwrite',
                '--allow-paths',
                str(self.project_dir),
                '-o',
                str(bin_outfile.parent),
                str(source_file)
            ]
            log.debug("Executing compiler with: {}".format(' '.join(compile_cmd)))

            abi_cmd = [
                SOLC_PATH,
                '--abi',
                '--overwrite',
                '--allow-paths',
                str(self.project_dir),
                '-o',
                str(abi_outfile.parent),
                str(source_file)
            ]
            log.debug("Executing compiler with: {}".format(' '.join(abi_cmd)))

            # Do the needful
            p_bin = Popen(compile_cmd, stdout=PIPE, stderr=STDOUT)
            p_abi = Popen(abi_cmd, stdout=PIPE, stderr=STDOUT)

            # Twiddle our thumbs
            p_bin.wait()
            p_abi.wait()

            # Check the output
            # p_bin_out = p_bin.stdout.read()
            # p_abi_out = p_abi.stdout.read()
            # solc version differences?
            # if (
            #     b'Compiler run successful' not in p_bin_out
            #     and b'Compiler run successful' not in p_abi_out
            #         ):
            #     log.error("Compiler shows an error:")
            #     raise CompileError("solc did not indicate success.")

            # Check the return codes
            compile_retval = p_bin.returncode
            abi_retval = p_abi.returncode
            if compile_retval != 0 or abi_retval != 0:
                raise CompileError("Solidity compiler returned non-zero exit code")

            if bin_outfile.stat().st_size == 0:
                raise CompileError(
                    "Zero length bytecode output from compiler. This has only been seen to occur if"
                    "  there was a silent error by the Solidity compileer"
                )

        elif ext == 'vy':

            source_text = ''

            with source_file.open() as _file:
                source_text = _file.read()

            if not source_text:
                # TODO: Do we want to die in a fire here?
                log.warning("Source file for {} appears to be empty!".format(name))
                return

            if is_vyper_interface(source_text):
                log.warning("{} appears to be a Vyper interface.  Skipping.".format(name))
                return

            # Read in the source for the interface(s)
            interface_imports = extract_file_interface_imports(source_text)
            interface_codes = dict()

            for interface_name, interface_path in interface_imports.items():
                interface_filepath = vyper_import_to_file_paths(self.dir, interface_path)
                with interface_filepath.open() as _file:
                    interface_codes[interface_name] = {
                        'type': 'vyper',
                        'code': _file.read()
                    }

            compiler_out = vyper.compile_code(
                source_text,
                ['bytecode', 'abi'],
                interface_codes=interface_codes,
            )

            if not compiler_out.get('bytecode') and not compiler_out.get('abi'):
                log.error("Nothing returned by vyper compiler for {}".format(name))
                return

            if not compiler_out.get('bytecode'):
                log.warning("No bytecode returned by vyper compiler for contract {}".format(name))
            else:

                # Create the output file and open for writing of bytecode
                with bin_outfile.open(mode='w') as out:
                    out.write(compiler_out['bytecode'])

            # ABI
            if not compiler_out.get('abi'):
                log.warning("No ABI returned by vyper compiler for contract {}".format(name))
            else:

                # Create the output file and open for writing of bytecode
                with abi_outfile.open(mode='w') as out:
                    out.write(json.dumps(compiler_out['abi']))

        else:
            raise CompileError("Unsupported source file type")