예제 #1
0
    def __init__(
            self,
            executable="solc",
            outputs: List[str]=None,
            optimize: Optional[int]=None,
            evm_version: Optional[str]=None,
            warnings_as_errors: bool=False):

        with RaiseForParam("executable"):
            isfile_assert(executable)
            self._executable = executable

        with RaiseForParam("outputs"):
            if outputs is not None:
                for choice in outputs:
                    OutputSelection.value_assert(choice)
                self._outputs = outputs[:]
            else:
                self._outputs = _make_default_output_selection()

        with RaiseForParam("optimize"):
            type_assert(optimize, (int, type(None)))
            self._optimize = optimize

        with RaiseForParam("evm_version"):
            if evm_version is not None:
                EvmVersion.value_assert(evm_version)
            self._evm_version = evm_version

        self._warnings_as_errors = bool(warnings_as_errors)
예제 #2
0
    def add_string(self, unitname: str, source: str) -> None:
        """Add a source string to the list of sources

        :param unitname: a name for the provided source unit
        :param source: source code text
        """
        with RaiseForParam("unitname"):
            type_assert(unitname, str)
        with RaiseForParam("source"):
            type_assert(source, str)
        self._text_sources[unitname] = source
예제 #3
0
    def select(self, selector: str) -> dict:
        """Find a single contract matching the contract selector string.

        The selector string is a string in either of the following forms:

        - "{suffix}:{contractname}": source unit name suffix and contract name,
            separated by ':'. Example: "erc20/ERC20.sol:ERC20" matches contract
            named "ERC20" in source unit "/home/user/contracts/erc20/ERC20.sol".

        - "{contractname}": only the contract name. Example: "ERC20" matches
            contract named "ERC20".

        If the selector matches multiple contract, this function will raise an
        exception of type ValueError.

        :param selector: contract selector
        """
        with RaiseForParam("selector"):
            type_assert(selector, str)
            if selector.count(":") == 1:
                suffix, contractname = selector.split(":")
            else:
                suffix, contractname = None, selector
            value_assert(
                "/" not in contractname,
                "Invalid contract selector syntax. Either 'path/suffix:ContractName' or 'ContractName'"
            )
            contracts = self.find(suffix, contractname)
            value_assert(len(contracts) != 0, "Contract not found")
            value_assert(
                len(contracts) == 1,
                "Contract selector matched multiple contracts")
            return self._contracts[contracts[0]]
예제 #4
0
    def add_file(self, path: str) -> None:
        """Add file to the list of sources.

        :param path: file path
        """
        with RaiseForParam("path"):
            type_assert(path, str)
        self._file_sources.append(path)
예제 #5
0
 def import_raw_key(self, private_key: str, passphrase: str = ""):
     with RaiseForParam("private_key"):
         value_assert(private_key.startswith("0x"),
                      "must be a hex string prefixed with 0x")
     account_address = self._web3.personal.importRawKey(
         private_key, passphrase)
     if account_address not in self._accounts:
         self._accounts.append(account_address)
예제 #6
0
    def update(self, other: "ContractObjectList") -> None:
        """Add all contracts from other ContractObjectList.

        :param other: other ContractObjectList with contracts to add
        """
        with RaiseForParam("other"):
            type_assert(other, ContractObjectList)
        for (unitname, contractname), contract in other._contracts.items():
            self.add_contract(unitname, contractname, contract)
예제 #7
0
    def add_files(self, sources: List[str]) -> None:
        """Add list of files to the list of sources.

        :param sources: list of file paths
        """
        with RaiseForParam("source"):
            type_assert(sources, list, ", paths to source files")
            for source in sources:
                type_assert(source, str, ", item in source file list")
        self._file_sources.extend(sources)
예제 #8
0
    def find(self, suffix: Optional[str],
             contractname: str) -> List[Tuple[str, str]]:
        """Find contracts by unitname suffix and full contractname.

        Example: ("erc20/ERC20.sol", "ERC20") matches ("/home/user/contracts/erc20/ERC20.sol", "ERC20").

        :param suffix: suffix to match the contract source unit name, or None; if it is None, any
            unit name is matched.
        :param contractname: full name of the contract
        """
        with RaiseForParam("suffix"):
            type_assert(suffix, (str, type(None)))
        with RaiseForParam("contractname"):
            type_assert(contractname, str)
        out = []
        units = self._name_to_units.get(contractname, [])
        for unit in units:
            if suffix is None or unit.endswith(suffix):
                out.append((unit, contractname))
        return out
예제 #9
0
    def add_directory(self,
                      path: str,
                      ext_filter: Optional[List[str]] = [".sol"]) -> None:
        """Add all sources from a directory.

        :param path: directory path
        :param ext_filter: list of allowed extensions for the source file names, including
            the '.' character (e.g. [".sol"]), or None for any extension
        """
        with RaiseForParam("path"):
            type_assert(path, str)
        for root, _dirnames, filenames in os.walk(path):
            for filename in filenames:
                if ext_filter is None or os.path.splitext(
                        filename)[1].lower() in ext_filter:
                    self._file_sources.append(os.path.join(root, filename))
예제 #10
0
def _DEBUG_SolcEmscripten(enable_emscripten):
    from solitude.tools import solc
    with RaiseForParam("enable_emscripten"):
        type_assert(enable_emscripten, bool)
    solc.Solc = solc.SolcEmscripten
예제 #11
0
    def compile(
            self,
            source_files: Optional[List[str]]=None,
            source_strings: Optional[Dict[str, str]]=None):

        cmd = [self._executable, "--standard-json"]
        data = _SolcStandardJsonInput()

        data.set_output_selection(self._outputs)
        if self._optimize is not None:
            data.set_optimizer(self._optimize)
        if self._evm_version is not None:
            data.set_evm_version(self._evm_version)

        # read source files and include them in the JSON
        unitname_to_path = {}
        with RaiseForParam("source_files"):
            if source_files is not None:
                type_assert(source_files, list)
                for path in source_files:
                    isfile_assert(path)
                    absolute_path = os.path.abspath(path)
                    unitname = data.add_source_from_file(absolute_path)
                    unitname_to_path[unitname] = absolute_path

        # include source strings in the JSON
        with RaiseForParam("source_strings"):
            if source_strings is not None:
                type_assert(source_strings, dict, ", (name: str -> code: str)")
                for unitname, contents in source_strings.items():
                    value_assert(
                        isinstance(unitname, str),
                        "Key must be a string containing a name")
                    value_assert(
                        not unitname.startswith("/"),
                        "Key cannot start with '/'")
                    value_assert(
                        isinstance(contents, str),
                        "Value must be a string containing the source code")
                    data.add_source(unitname, contents)

        # invoke solc
        solc_out = self.call_standard_json(data.value())
        timestamp = datetime.datetime.utcnow().isoformat()

        # collect errors and warnings
        errors = []
        warnings = []
        for error in solc_out.get("errors", []):
            severity = error.get("severity")
            if severity == "error" or self._warnings_as_errors:
                errors.append(error)
            else:
                warnings.append(error)

        # raise exception on error
        if errors:
            raise CompilerError([
                _convert_error(error) for error in errors])

        contracts = solc_out.get("contracts", {})
        sources = solc_out.get("sources", {})
        sourceid_to_unitname = [None] * len(sources)

        output = {}

        # gather AST and source id for each source
        unitname_to_ast = {}
        for unitname, unit in sources.items():
            unitname_to_ast[unitname] = unit.get("ast")
            sourceid_to_unitname[unit["id"]] = unitname

        # create output dictionary, (unitname, contractname) -> contract
        # including the full source content and AST
        for unitname, contracts_in_unit in contracts.items():
            for contractname, contract in contracts_in_unit.items():
                try:
                    source_path = unitname_to_path[unitname]
                except KeyError:
                    source_path = unitname
                out_contract_data = {}
                out_contract_data["abi"] = contract.get("abi")
                out_contract_data["bin"] = (
                    contract.get("evm", {}).get("bytecode", {}).get("object"))
                out_contract_data["srcmap"] = (
                    contract.get("evm", {}).get("bytecode", {}).get("sourceMap"))
                out_contract_data["bin-runtime"] = (
                    contract.get("evm", {}).get("deployedBytecode", {}).get("object"))
                out_contract_data["srcmap-runtime"] = (
                    contract.get("evm", {}).get("deployedBytecode", {}).get("sourceMap"))
                out_contract_data["_solitude"] = {
                    "ast": unitname_to_ast[unitname],
                    "unitName": unitname,
                    "contractName": contractname,
                    "sourceList": sourceid_to_unitname,
                    "sourcePath": source_path,
                    "source": data.get_source(unitname),
                    "timestamp": timestamp
                }
                output[(unitname, contractname)] = out_contract_data
        return output