crytic_compile.platform.dapp
Dapp platform. https://github.com/dapphub/dapptools
1""" 2Dapp platform. https://github.com/dapphub/dapptools 3""" 4 5import glob 6import json 7import logging 8import os 9import re 10import shutil 11import subprocess 12from pathlib import Path 13 14# Cycle dependency 15from typing import TYPE_CHECKING, List, Optional 16 17from crytic_compile.compilation_unit import CompilationUnit 18from crytic_compile.compiler.compiler import CompilerVersion 19from crytic_compile.platform.abstract_platform import AbstractPlatform 20from crytic_compile.platform.types import Type 21from crytic_compile.utils.naming import convert_filename, extract_name 22 23# Handle cycle 24from crytic_compile.utils.natspec import Natspec 25from crytic_compile.utils.subprocess import run 26 27if TYPE_CHECKING: 28 from crytic_compile import CryticCompile 29 30LOGGER = logging.getLogger("CryticCompile") 31 32 33class Dapp(AbstractPlatform): 34 """ 35 Dapp class 36 """ 37 38 NAME = "Dapp" 39 PROJECT_URL = "https://github.com/dapphub/dapptools" 40 TYPE = Type.DAPP 41 42 # pylint: disable=too-many-locals 43 def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None: 44 """Run the compilation 45 46 Args: 47 crytic_compile (CryticCompile): Associated CryticCompile object 48 **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile" 49 """ 50 51 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 52 "ignore_compile", False 53 ) 54 directory = os.path.join(self._target, "out") 55 56 if not dapp_ignore_compile: 57 _run_dapp(self._target) 58 59 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 60 61 compilation_unit.compiler_version = _get_version(self._target) 62 63 optimized = False 64 65 with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc: 66 targets_json = json.load(file_desc) 67 68 version: Optional[str] = None 69 if "version" in targets_json: 70 version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0] 71 72 for path, info in targets_json["sources"].items(): 73 path = convert_filename( 74 path, _relative_to_short, crytic_compile, working_dir=self._target 75 ) 76 source_unit = compilation_unit.create_source_unit(path) 77 source_unit.ast = info["ast"] 78 79 for original_filename, contracts_info in targets_json["contracts"].items(): 80 81 filename = convert_filename( 82 original_filename, lambda x: x, crytic_compile, self._target 83 ) 84 85 source_unit = compilation_unit.create_source_unit(filename) 86 87 for original_contract_name, info in contracts_info.items(): 88 if "metadata" in info: 89 metadata = json.loads(info["metadata"]) 90 if ( 91 "settings" in metadata 92 and "optimizer" in metadata["settings"] 93 and "enabled" in metadata["settings"]["optimizer"] 94 ): 95 optimized |= metadata["settings"]["optimizer"]["enabled"] 96 contract_name = extract_name(original_contract_name) 97 source_unit.add_contract_name(contract_name) 98 compilation_unit.filename_to_contracts[filename].add(contract_name) 99 100 source_unit.abis[contract_name] = info["abi"] 101 source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"] 102 source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][ 103 "object" 104 ] 105 source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][ 106 "sourceMap" 107 ].split(";") 108 source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][ 109 "sourceMap" 110 ].split(";") 111 userdoc = info.get("userdoc", {}) 112 devdoc = info.get("devdoc", {}) 113 natspec = Natspec(userdoc, devdoc) 114 source_unit.natspec[contract_name] = natspec 115 116 if version is None: 117 metadata = json.loads(info["metadata"]) 118 version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0] 119 120 compilation_unit.compiler_version = CompilerVersion( 121 compiler="solc", version=version, optimized=optimized 122 ) 123 124 def clean(self, **kwargs: str) -> None: 125 """Clean compilation artifacts 126 127 Args: 128 **kwargs: optional arguments. 129 """ 130 131 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 132 "ignore_compile", False 133 ) 134 if dapp_ignore_compile: 135 return 136 137 run(["dapp", "clean"], cwd=self._target) 138 139 @staticmethod 140 def is_supported(target: str, **kwargs: str) -> bool: 141 """Check if the target is a dapp project 142 143 Args: 144 target (str): path to the target 145 **kwargs: optional arguments. Used: "dapp_ignore" 146 147 Returns: 148 bool: True if the target is a dapp project 149 """ 150 dapp_ignore = kwargs.get("dapp_ignore", False) 151 if dapp_ignore: 152 return False 153 makefile = os.path.join(target, "Makefile") 154 if os.path.isfile(makefile): 155 with open(makefile, encoding="utf8") as file_desc: 156 txt = file_desc.read() 157 return "dapp " in txt 158 return False 159 160 def is_dependency(self, path: str) -> bool: 161 """Check if the path is a dependency (not supported for brownie) 162 163 Args: 164 path (str): path to the target 165 166 Returns: 167 bool: True if the target is a dependency 168 """ 169 if path in self._cached_dependencies: 170 return self._cached_dependencies[path] 171 ret = "node_modules" in Path(path).parts 172 self._cached_dependencies[path] = ret 173 return "lib" in Path(path).parts 174 175 def _guessed_tests(self) -> List[str]: 176 """Guess the potential unit tests commands 177 178 Returns: 179 List[str]: The guessed unit tests commands 180 """ 181 return ["dapp test"] 182 183 184def _run_dapp(target: str) -> None: 185 """Run the dapp compilation 186 187 Args: 188 target (str): path to the target 189 190 Raises: 191 InvalidCompilation: If dapp failed to run 192 """ 193 # pylint: disable=import-outside-toplevel 194 from crytic_compile.platform.exceptions import InvalidCompilation 195 196 cmd = ["dapp", "build"] 197 198 try: 199 with subprocess.Popen( 200 cmd, 201 stdout=subprocess.PIPE, 202 stderr=subprocess.PIPE, 203 cwd=target, 204 executable=shutil.which(cmd[0]), 205 ) as process: 206 _, _ = process.communicate() 207 except OSError as error: 208 # pylint: disable=raise-missing-from 209 raise InvalidCompilation(error) 210 211 212def _get_version(target: str) -> CompilerVersion: 213 """Get the compiler version 214 215 Args: 216 target (str): path to the target 217 218 Returns: 219 CompilerVersion: compiler information 220 """ 221 files = glob.glob(target + "/**/*meta.json", recursive=True) 222 version: Optional[str] = None 223 optimized = None 224 compiler = "solc" 225 for file in files: 226 if version is None: 227 with open(file, encoding="utf8") as file_desc: 228 config = json.load(file_desc) 229 if "compiler" in config: 230 if "version" in config["compiler"]: 231 versions = re.findall(r"\d+\.\d+\.\d+", config["compiler"]["version"]) 232 if versions: 233 version = versions[0] 234 if "settings" in config: 235 if "optimizer" in config["settings"]: 236 if "enabled" in config["settings"]["optimizer"]: 237 optimized = config["settings"]["optimizer"]["enabled"] 238 239 return CompilerVersion(compiler=compiler, version=version, optimized=optimized) 240 241 242def _relative_to_short(relative: Path) -> Path: 243 """Translate relative path to short (do nothing for brownie) 244 245 Args: 246 relative (Path): path to the target 247 248 Returns: 249 Path: Translated path 250 """ 251 short = relative 252 try: 253 short = short.relative_to(Path("src")) 254 except ValueError: 255 try: 256 short = short.relative_to("lib") 257 except ValueError: 258 pass 259 return short
LOGGER =
<Logger CryticCompile (WARNING)>
34class Dapp(AbstractPlatform): 35 """ 36 Dapp class 37 """ 38 39 NAME = "Dapp" 40 PROJECT_URL = "https://github.com/dapphub/dapptools" 41 TYPE = Type.DAPP 42 43 # pylint: disable=too-many-locals 44 def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None: 45 """Run the compilation 46 47 Args: 48 crytic_compile (CryticCompile): Associated CryticCompile object 49 **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile" 50 """ 51 52 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 53 "ignore_compile", False 54 ) 55 directory = os.path.join(self._target, "out") 56 57 if not dapp_ignore_compile: 58 _run_dapp(self._target) 59 60 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 61 62 compilation_unit.compiler_version = _get_version(self._target) 63 64 optimized = False 65 66 with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc: 67 targets_json = json.load(file_desc) 68 69 version: Optional[str] = None 70 if "version" in targets_json: 71 version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0] 72 73 for path, info in targets_json["sources"].items(): 74 path = convert_filename( 75 path, _relative_to_short, crytic_compile, working_dir=self._target 76 ) 77 source_unit = compilation_unit.create_source_unit(path) 78 source_unit.ast = info["ast"] 79 80 for original_filename, contracts_info in targets_json["contracts"].items(): 81 82 filename = convert_filename( 83 original_filename, lambda x: x, crytic_compile, self._target 84 ) 85 86 source_unit = compilation_unit.create_source_unit(filename) 87 88 for original_contract_name, info in contracts_info.items(): 89 if "metadata" in info: 90 metadata = json.loads(info["metadata"]) 91 if ( 92 "settings" in metadata 93 and "optimizer" in metadata["settings"] 94 and "enabled" in metadata["settings"]["optimizer"] 95 ): 96 optimized |= metadata["settings"]["optimizer"]["enabled"] 97 contract_name = extract_name(original_contract_name) 98 source_unit.add_contract_name(contract_name) 99 compilation_unit.filename_to_contracts[filename].add(contract_name) 100 101 source_unit.abis[contract_name] = info["abi"] 102 source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"] 103 source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][ 104 "object" 105 ] 106 source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][ 107 "sourceMap" 108 ].split(";") 109 source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][ 110 "sourceMap" 111 ].split(";") 112 userdoc = info.get("userdoc", {}) 113 devdoc = info.get("devdoc", {}) 114 natspec = Natspec(userdoc, devdoc) 115 source_unit.natspec[contract_name] = natspec 116 117 if version is None: 118 metadata = json.loads(info["metadata"]) 119 version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0] 120 121 compilation_unit.compiler_version = CompilerVersion( 122 compiler="solc", version=version, optimized=optimized 123 ) 124 125 def clean(self, **kwargs: str) -> None: 126 """Clean compilation artifacts 127 128 Args: 129 **kwargs: optional arguments. 130 """ 131 132 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 133 "ignore_compile", False 134 ) 135 if dapp_ignore_compile: 136 return 137 138 run(["dapp", "clean"], cwd=self._target) 139 140 @staticmethod 141 def is_supported(target: str, **kwargs: str) -> bool: 142 """Check if the target is a dapp project 143 144 Args: 145 target (str): path to the target 146 **kwargs: optional arguments. Used: "dapp_ignore" 147 148 Returns: 149 bool: True if the target is a dapp project 150 """ 151 dapp_ignore = kwargs.get("dapp_ignore", False) 152 if dapp_ignore: 153 return False 154 makefile = os.path.join(target, "Makefile") 155 if os.path.isfile(makefile): 156 with open(makefile, encoding="utf8") as file_desc: 157 txt = file_desc.read() 158 return "dapp " in txt 159 return False 160 161 def is_dependency(self, path: str) -> bool: 162 """Check if the path is a dependency (not supported for brownie) 163 164 Args: 165 path (str): path to the target 166 167 Returns: 168 bool: True if the target is a dependency 169 """ 170 if path in self._cached_dependencies: 171 return self._cached_dependencies[path] 172 ret = "node_modules" in Path(path).parts 173 self._cached_dependencies[path] = ret 174 return "lib" in Path(path).parts 175 176 def _guessed_tests(self) -> List[str]: 177 """Guess the potential unit tests commands 178 179 Returns: 180 List[str]: The guessed unit tests commands 181 """ 182 return ["dapp test"]
Dapp class
def
compile( self, crytic_compile: crytic_compile.crytic_compile.CryticCompile, **kwargs: str) -> None:
44 def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None: 45 """Run the compilation 46 47 Args: 48 crytic_compile (CryticCompile): Associated CryticCompile object 49 **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile" 50 """ 51 52 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 53 "ignore_compile", False 54 ) 55 directory = os.path.join(self._target, "out") 56 57 if not dapp_ignore_compile: 58 _run_dapp(self._target) 59 60 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 61 62 compilation_unit.compiler_version = _get_version(self._target) 63 64 optimized = False 65 66 with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc: 67 targets_json = json.load(file_desc) 68 69 version: Optional[str] = None 70 if "version" in targets_json: 71 version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0] 72 73 for path, info in targets_json["sources"].items(): 74 path = convert_filename( 75 path, _relative_to_short, crytic_compile, working_dir=self._target 76 ) 77 source_unit = compilation_unit.create_source_unit(path) 78 source_unit.ast = info["ast"] 79 80 for original_filename, contracts_info in targets_json["contracts"].items(): 81 82 filename = convert_filename( 83 original_filename, lambda x: x, crytic_compile, self._target 84 ) 85 86 source_unit = compilation_unit.create_source_unit(filename) 87 88 for original_contract_name, info in contracts_info.items(): 89 if "metadata" in info: 90 metadata = json.loads(info["metadata"]) 91 if ( 92 "settings" in metadata 93 and "optimizer" in metadata["settings"] 94 and "enabled" in metadata["settings"]["optimizer"] 95 ): 96 optimized |= metadata["settings"]["optimizer"]["enabled"] 97 contract_name = extract_name(original_contract_name) 98 source_unit.add_contract_name(contract_name) 99 compilation_unit.filename_to_contracts[filename].add(contract_name) 100 101 source_unit.abis[contract_name] = info["abi"] 102 source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"] 103 source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][ 104 "object" 105 ] 106 source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][ 107 "sourceMap" 108 ].split(";") 109 source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][ 110 "sourceMap" 111 ].split(";") 112 userdoc = info.get("userdoc", {}) 113 devdoc = info.get("devdoc", {}) 114 natspec = Natspec(userdoc, devdoc) 115 source_unit.natspec[contract_name] = natspec 116 117 if version is None: 118 metadata = json.loads(info["metadata"]) 119 version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0] 120 121 compilation_unit.compiler_version = CompilerVersion( 122 compiler="solc", version=version, optimized=optimized 123 )
Run the compilation
Args: crytic_compile (CryticCompile): Associated CryticCompile object **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile"
def
clean(self, **kwargs: str) -> None:
125 def clean(self, **kwargs: str) -> None: 126 """Clean compilation artifacts 127 128 Args: 129 **kwargs: optional arguments. 130 """ 131 132 dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get( 133 "ignore_compile", False 134 ) 135 if dapp_ignore_compile: 136 return 137 138 run(["dapp", "clean"], cwd=self._target)
Clean compilation artifacts
Args: **kwargs: optional arguments.
@staticmethod
def
is_supported(target: str, **kwargs: str) -> bool:
140 @staticmethod 141 def is_supported(target: str, **kwargs: str) -> bool: 142 """Check if the target is a dapp project 143 144 Args: 145 target (str): path to the target 146 **kwargs: optional arguments. Used: "dapp_ignore" 147 148 Returns: 149 bool: True if the target is a dapp project 150 """ 151 dapp_ignore = kwargs.get("dapp_ignore", False) 152 if dapp_ignore: 153 return False 154 makefile = os.path.join(target, "Makefile") 155 if os.path.isfile(makefile): 156 with open(makefile, encoding="utf8") as file_desc: 157 txt = file_desc.read() 158 return "dapp " in txt 159 return False
Check if the target is a dapp project
Args: target (str): path to the target **kwargs: optional arguments. Used: "dapp_ignore"
Returns: bool: True if the target is a dapp project
def
is_dependency(self, path: str) -> bool:
161 def is_dependency(self, path: str) -> bool: 162 """Check if the path is a dependency (not supported for brownie) 163 164 Args: 165 path (str): path to the target 166 167 Returns: 168 bool: True if the target is a dependency 169 """ 170 if path in self._cached_dependencies: 171 return self._cached_dependencies[path] 172 ret = "node_modules" in Path(path).parts 173 self._cached_dependencies[path] = ret 174 return "lib" in Path(path).parts
Check if the path is a dependency (not supported for brownie)
Args: path (str): path to the target
Returns: bool: True if the target is a dependency