crytic_compile.platform.etherlime
Etherlime platform. https://github.com/LimeChain/etherlime
1""" 2Etherlime platform. https://github.com/LimeChain/etherlime 3""" 4 5import glob 6import json 7import logging 8import os 9import re 10import shutil 11import subprocess 12from pathlib import Path 13from typing import TYPE_CHECKING, List, Optional, Any 14 15from crytic_compile.compilation_unit import CompilationUnit 16from crytic_compile.compiler.compiler import CompilerVersion 17from crytic_compile.platform.abstract_platform import AbstractPlatform 18from crytic_compile.platform.exceptions import InvalidCompilation 19from crytic_compile.platform.types import Type 20from crytic_compile.utils.naming import convert_filename 21 22# Cycle dependency 23from crytic_compile.utils.natspec import Natspec 24 25if TYPE_CHECKING: 26 from crytic_compile import CryticCompile 27 28LOGGER = logging.getLogger("CryticCompile") 29 30 31def _run_etherlime(target: str, npx_disable: bool, compile_arguments: Optional[str]) -> None: 32 """Run etherlime 33 34 Args: 35 target (str): path to the target 36 npx_disable (bool): true if npx should not be used 37 compile_arguments (Optional[str]): additional arguments 38 39 Raises: 40 InvalidCompilation: if etherlime fails 41 """ 42 cmd = ["etherlime", "compile", target, "deleteCompiledFiles=true"] 43 44 if not npx_disable: 45 cmd = ["npx"] + cmd 46 47 if compile_arguments: 48 cmd += compile_arguments.split(" ") 49 50 try: 51 with subprocess.Popen( 52 cmd, 53 stdout=subprocess.PIPE, 54 stderr=subprocess.PIPE, 55 cwd=target, 56 executable=shutil.which(cmd[0]), 57 ) as process: 58 stdout_bytes, stderr_bytes = process.communicate() 59 stdout, stderr = ( 60 stdout_bytes.decode(errors="backslashreplace"), 61 stderr_bytes.decode(errors="backslashreplace"), 62 ) # convert bytestrings to unicode strings 63 64 LOGGER.info(stdout) 65 66 if stderr: 67 LOGGER.error(stderr) 68 except OSError as error: 69 # pylint: disable=raise-missing-from 70 raise InvalidCompilation(error) 71 72 73class Etherlime(AbstractPlatform): 74 """ 75 Etherlime platform 76 """ 77 78 NAME = "Etherlime" 79 PROJECT_URL = "https://github.com/LimeChain/etherlime" 80 TYPE = Type.ETHERLIME 81 82 # pylint: disable=too-many-locals 83 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 84 """Run the compilation 85 86 Args: 87 crytic_compile (CryticCompile): Associated CryticCompile object 88 **kwargs: optional arguments. Used "etherlime_ignore_compile", "ignore_compile" 89 90 Raises: 91 InvalidCompilation: if etherlime failed to run 92 """ 93 94 etherlime_ignore_compile = kwargs.get("etherlime_ignore_compile", False) or kwargs.get( 95 "ignore_compile", False 96 ) 97 98 build_directory = "build" 99 compile_arguments: Optional[str] = kwargs.get("etherlime_compile_arguments", None) 100 npx_disable: bool = kwargs.get("npx_disable", False) 101 102 if not etherlime_ignore_compile: 103 _run_etherlime(self._target, npx_disable, compile_arguments) 104 105 # similar to truffle 106 if not os.path.isdir(os.path.join(self._target, build_directory)): 107 raise InvalidCompilation( 108 "No truffle build directory found, did you run `truffle compile`?" 109 ) 110 filenames = glob.glob(os.path.join(self._target, build_directory, "*.json")) 111 112 version = None 113 compiler = "solc-js" 114 115 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 116 117 for file in filenames: 118 with open(file, encoding="utf8") as file_desc: 119 target_loaded = json.load(file_desc) 120 121 if version is None: 122 if "compiler" in target_loaded: 123 if "version" in target_loaded["compiler"]: 124 version = re.findall( 125 r"\d+\.\d+\.\d+", target_loaded["compiler"]["version"] 126 )[0] 127 128 if "ast" not in target_loaded: 129 continue 130 131 filename_txt = target_loaded["ast"]["absolutePath"] 132 filename = convert_filename(filename_txt, _relative_to_short, crytic_compile) 133 134 source_unit = compilation_unit.create_source_unit(filename) 135 136 source_unit.ast = target_loaded["ast"] 137 contract_name = target_loaded["contractName"] 138 139 compilation_unit.filename_to_contracts[filename].add(contract_name) 140 source_unit.add_contract_name(contract_name) 141 source_unit.abis[contract_name] = target_loaded["abi"] 142 source_unit.bytecodes_init[contract_name] = target_loaded["bytecode"].replace( 143 "0x", "" 144 ) 145 source_unit.bytecodes_runtime[contract_name] = target_loaded[ 146 "deployedBytecode" 147 ].replace("0x", "") 148 source_unit.srcmaps_init[contract_name] = target_loaded["sourceMap"].split(";") 149 source_unit.srcmaps_runtime[contract_name] = target_loaded[ 150 "deployedSourceMap" 151 ].split(";") 152 153 userdoc = target_loaded.get("userdoc", {}) 154 devdoc = target_loaded.get("devdoc", {}) 155 natspec = Natspec(userdoc, devdoc) 156 source_unit.natspec[contract_name] = natspec 157 158 compilation_unit.compiler_version = CompilerVersion( 159 compiler=compiler, version=version, optimized=_is_optimized(compile_arguments) 160 ) 161 162 def clean(self, **_kwargs: str) -> None: 163 # TODO: research if there's a way to clean artifacts 164 pass 165 166 @staticmethod 167 def is_supported(target: str, **kwargs: str) -> bool: 168 """Check if the target is an etherlime project 169 170 Args: 171 target (str): path to the target 172 **kwargs: optional arguments. Used "etherlime_ignore" 173 174 Returns: 175 bool: True if the target is a etherlime project 176 """ 177 etherlime_ignore = kwargs.get("etherlime_ignore", False) 178 if etherlime_ignore: 179 return False 180 if os.path.isfile(os.path.join(target, "package.json")): 181 with open(os.path.join(target, "package.json"), encoding="utf8") as file_desc: 182 package = json.load(file_desc) 183 if "dependencies" in package: 184 return ( 185 "etherlime-lib" in package["dependencies"] 186 or "etherlime" in package["dependencies"] 187 ) 188 if "devDependencies" in package: 189 return ( 190 "etherlime-lib" in package["devDependencies"] 191 or "etherlime" in package["devDependencies"] 192 ) 193 return False 194 195 def is_dependency(self, path: str) -> bool: 196 """Check if the path is a dependency 197 198 Args: 199 path (str): path to the target 200 201 Returns: 202 bool: True if the target is a dependency 203 """ 204 if path in self._cached_dependencies: 205 return self._cached_dependencies[path] 206 ret = "node_modules" in Path(path).parts 207 self._cached_dependencies[path] = ret 208 return ret 209 210 def _guessed_tests(self) -> List[str]: 211 """Guess the potential unit tests commands 212 213 Returns: 214 List[str]: The guessed unit tests commands 215 """ 216 return ["etherlime test"] 217 218 219def _is_optimized(compile_arguments: Optional[str]) -> bool: 220 """Check if the optimization is enabled 221 222 Args: 223 compile_arguments (Optional[str]): list of compilation arguments 224 225 Returns: 226 bool: True if the optimizations are enabled 227 """ 228 if compile_arguments: 229 return "--run" in compile_arguments 230 return False 231 232 233def _relative_to_short(relative: Path) -> Path: 234 """Translate relative path to short 235 236 Args: 237 relative (Path): path to the target 238 239 Returns: 240 Path: Translated path 241 """ 242 short = relative 243 try: 244 short = short.relative_to(Path("contracts")) 245 except ValueError: 246 try: 247 short = short.relative_to("node_modules") 248 except ValueError: 249 pass 250 return short
LOGGER =
<Logger CryticCompile (WARNING)>
74class Etherlime(AbstractPlatform): 75 """ 76 Etherlime platform 77 """ 78 79 NAME = "Etherlime" 80 PROJECT_URL = "https://github.com/LimeChain/etherlime" 81 TYPE = Type.ETHERLIME 82 83 # pylint: disable=too-many-locals 84 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 85 """Run the compilation 86 87 Args: 88 crytic_compile (CryticCompile): Associated CryticCompile object 89 **kwargs: optional arguments. Used "etherlime_ignore_compile", "ignore_compile" 90 91 Raises: 92 InvalidCompilation: if etherlime failed to run 93 """ 94 95 etherlime_ignore_compile = kwargs.get("etherlime_ignore_compile", False) or kwargs.get( 96 "ignore_compile", False 97 ) 98 99 build_directory = "build" 100 compile_arguments: Optional[str] = kwargs.get("etherlime_compile_arguments", None) 101 npx_disable: bool = kwargs.get("npx_disable", False) 102 103 if not etherlime_ignore_compile: 104 _run_etherlime(self._target, npx_disable, compile_arguments) 105 106 # similar to truffle 107 if not os.path.isdir(os.path.join(self._target, build_directory)): 108 raise InvalidCompilation( 109 "No truffle build directory found, did you run `truffle compile`?" 110 ) 111 filenames = glob.glob(os.path.join(self._target, build_directory, "*.json")) 112 113 version = None 114 compiler = "solc-js" 115 116 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 117 118 for file in filenames: 119 with open(file, encoding="utf8") as file_desc: 120 target_loaded = json.load(file_desc) 121 122 if version is None: 123 if "compiler" in target_loaded: 124 if "version" in target_loaded["compiler"]: 125 version = re.findall( 126 r"\d+\.\d+\.\d+", target_loaded["compiler"]["version"] 127 )[0] 128 129 if "ast" not in target_loaded: 130 continue 131 132 filename_txt = target_loaded["ast"]["absolutePath"] 133 filename = convert_filename(filename_txt, _relative_to_short, crytic_compile) 134 135 source_unit = compilation_unit.create_source_unit(filename) 136 137 source_unit.ast = target_loaded["ast"] 138 contract_name = target_loaded["contractName"] 139 140 compilation_unit.filename_to_contracts[filename].add(contract_name) 141 source_unit.add_contract_name(contract_name) 142 source_unit.abis[contract_name] = target_loaded["abi"] 143 source_unit.bytecodes_init[contract_name] = target_loaded["bytecode"].replace( 144 "0x", "" 145 ) 146 source_unit.bytecodes_runtime[contract_name] = target_loaded[ 147 "deployedBytecode" 148 ].replace("0x", "") 149 source_unit.srcmaps_init[contract_name] = target_loaded["sourceMap"].split(";") 150 source_unit.srcmaps_runtime[contract_name] = target_loaded[ 151 "deployedSourceMap" 152 ].split(";") 153 154 userdoc = target_loaded.get("userdoc", {}) 155 devdoc = target_loaded.get("devdoc", {}) 156 natspec = Natspec(userdoc, devdoc) 157 source_unit.natspec[contract_name] = natspec 158 159 compilation_unit.compiler_version = CompilerVersion( 160 compiler=compiler, version=version, optimized=_is_optimized(compile_arguments) 161 ) 162 163 def clean(self, **_kwargs: str) -> None: 164 # TODO: research if there's a way to clean artifacts 165 pass 166 167 @staticmethod 168 def is_supported(target: str, **kwargs: str) -> bool: 169 """Check if the target is an etherlime project 170 171 Args: 172 target (str): path to the target 173 **kwargs: optional arguments. Used "etherlime_ignore" 174 175 Returns: 176 bool: True if the target is a etherlime project 177 """ 178 etherlime_ignore = kwargs.get("etherlime_ignore", False) 179 if etherlime_ignore: 180 return False 181 if os.path.isfile(os.path.join(target, "package.json")): 182 with open(os.path.join(target, "package.json"), encoding="utf8") as file_desc: 183 package = json.load(file_desc) 184 if "dependencies" in package: 185 return ( 186 "etherlime-lib" in package["dependencies"] 187 or "etherlime" in package["dependencies"] 188 ) 189 if "devDependencies" in package: 190 return ( 191 "etherlime-lib" in package["devDependencies"] 192 or "etherlime" in package["devDependencies"] 193 ) 194 return False 195 196 def is_dependency(self, path: str) -> bool: 197 """Check if the path is a dependency 198 199 Args: 200 path (str): path to the target 201 202 Returns: 203 bool: True if the target is a dependency 204 """ 205 if path in self._cached_dependencies: 206 return self._cached_dependencies[path] 207 ret = "node_modules" in Path(path).parts 208 self._cached_dependencies[path] = ret 209 return ret 210 211 def _guessed_tests(self) -> List[str]: 212 """Guess the potential unit tests commands 213 214 Returns: 215 List[str]: The guessed unit tests commands 216 """ 217 return ["etherlime test"]
Etherlime platform
def
compile( self, crytic_compile: crytic_compile.crytic_compile.CryticCompile, **kwargs: Any) -> None:
84 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 85 """Run the compilation 86 87 Args: 88 crytic_compile (CryticCompile): Associated CryticCompile object 89 **kwargs: optional arguments. Used "etherlime_ignore_compile", "ignore_compile" 90 91 Raises: 92 InvalidCompilation: if etherlime failed to run 93 """ 94 95 etherlime_ignore_compile = kwargs.get("etherlime_ignore_compile", False) or kwargs.get( 96 "ignore_compile", False 97 ) 98 99 build_directory = "build" 100 compile_arguments: Optional[str] = kwargs.get("etherlime_compile_arguments", None) 101 npx_disable: bool = kwargs.get("npx_disable", False) 102 103 if not etherlime_ignore_compile: 104 _run_etherlime(self._target, npx_disable, compile_arguments) 105 106 # similar to truffle 107 if not os.path.isdir(os.path.join(self._target, build_directory)): 108 raise InvalidCompilation( 109 "No truffle build directory found, did you run `truffle compile`?" 110 ) 111 filenames = glob.glob(os.path.join(self._target, build_directory, "*.json")) 112 113 version = None 114 compiler = "solc-js" 115 116 compilation_unit = CompilationUnit(crytic_compile, str(self._target)) 117 118 for file in filenames: 119 with open(file, encoding="utf8") as file_desc: 120 target_loaded = json.load(file_desc) 121 122 if version is None: 123 if "compiler" in target_loaded: 124 if "version" in target_loaded["compiler"]: 125 version = re.findall( 126 r"\d+\.\d+\.\d+", target_loaded["compiler"]["version"] 127 )[0] 128 129 if "ast" not in target_loaded: 130 continue 131 132 filename_txt = target_loaded["ast"]["absolutePath"] 133 filename = convert_filename(filename_txt, _relative_to_short, crytic_compile) 134 135 source_unit = compilation_unit.create_source_unit(filename) 136 137 source_unit.ast = target_loaded["ast"] 138 contract_name = target_loaded["contractName"] 139 140 compilation_unit.filename_to_contracts[filename].add(contract_name) 141 source_unit.add_contract_name(contract_name) 142 source_unit.abis[contract_name] = target_loaded["abi"] 143 source_unit.bytecodes_init[contract_name] = target_loaded["bytecode"].replace( 144 "0x", "" 145 ) 146 source_unit.bytecodes_runtime[contract_name] = target_loaded[ 147 "deployedBytecode" 148 ].replace("0x", "") 149 source_unit.srcmaps_init[contract_name] = target_loaded["sourceMap"].split(";") 150 source_unit.srcmaps_runtime[contract_name] = target_loaded[ 151 "deployedSourceMap" 152 ].split(";") 153 154 userdoc = target_loaded.get("userdoc", {}) 155 devdoc = target_loaded.get("devdoc", {}) 156 natspec = Natspec(userdoc, devdoc) 157 source_unit.natspec[contract_name] = natspec 158 159 compilation_unit.compiler_version = CompilerVersion( 160 compiler=compiler, version=version, optimized=_is_optimized(compile_arguments) 161 )
Run the compilation
Args: crytic_compile (CryticCompile): Associated CryticCompile object **kwargs: optional arguments. Used "etherlime_ignore_compile", "ignore_compile"
Raises: InvalidCompilation: if etherlime failed to run
def
clean(self, **_kwargs: str) -> None:
163 def clean(self, **_kwargs: str) -> None: 164 # TODO: research if there's a way to clean artifacts 165 pass
Clean compilation artifacts
Args: **kwargs: optional arguments.
@staticmethod
def
is_supported(target: str, **kwargs: str) -> bool:
167 @staticmethod 168 def is_supported(target: str, **kwargs: str) -> bool: 169 """Check if the target is an etherlime project 170 171 Args: 172 target (str): path to the target 173 **kwargs: optional arguments. Used "etherlime_ignore" 174 175 Returns: 176 bool: True if the target is a etherlime project 177 """ 178 etherlime_ignore = kwargs.get("etherlime_ignore", False) 179 if etherlime_ignore: 180 return False 181 if os.path.isfile(os.path.join(target, "package.json")): 182 with open(os.path.join(target, "package.json"), encoding="utf8") as file_desc: 183 package = json.load(file_desc) 184 if "dependencies" in package: 185 return ( 186 "etherlime-lib" in package["dependencies"] 187 or "etherlime" in package["dependencies"] 188 ) 189 if "devDependencies" in package: 190 return ( 191 "etherlime-lib" in package["devDependencies"] 192 or "etherlime" in package["devDependencies"] 193 ) 194 return False
Check if the target is an etherlime project
Args: target (str): path to the target **kwargs: optional arguments. Used "etherlime_ignore"
Returns: bool: True if the target is a etherlime project
def
is_dependency(self, path: str) -> bool:
196 def is_dependency(self, path: str) -> bool: 197 """Check if the path is a dependency 198 199 Args: 200 path (str): path to the target 201 202 Returns: 203 bool: True if the target is a dependency 204 """ 205 if path in self._cached_dependencies: 206 return self._cached_dependencies[path] 207 ret = "node_modules" in Path(path).parts 208 self._cached_dependencies[path] = ret 209 return ret
Check if the path is a dependency
Args: path (str): path to the target
Returns: bool: True if the target is a dependency