From dc0159772d10b3884e0b3e11f8baf8f6a23fc594 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sat, 21 Jul 2018 15:50:07 +0530 Subject: [PATCH 01/15] Add dynamic loading to config.ini --- mythril/interfaces/cli.py | 16 +++---- mythril/mythril.py | 93 ++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index b9a910e0..888816d3 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -84,12 +84,6 @@ def main(): args = parser.parse_args() - # -- args sanity checks -- - # Detect unsupported combinations of command line args - - if args.dynld and not args.address: - exit_with_error(args.outform, "Dynamic loader can be used in on-chain analysis mode only (-a).") - # Parse cmdline args if not (args.search or args.hash or args.disassemble or args.graph or args.fire_lasers @@ -111,12 +105,13 @@ def main(): try: # the mythril object should be our main interface - #infura = None, rpc = None, rpctls = None, ipc = None, - #solc_args = None, dynld = None, max_recursion_depth = 12): - + # infura = None, rpc = None, rpctls = None, ipc = None, + # solc_args = None, dynld = None, max_recursion_depth = 12): mythril = Mythril(solv=args.solv, dynld=args.dynld, solc_args=args.solc_args) + if args.dynld and not (args.ipc or args.rpc or args.i): + mythril.set_api_from_config_path() if args.address and not args.leveldb: # Establish RPC/IPC connection if necessary @@ -157,7 +152,7 @@ def main(): address, _ = mythril.load_from_address(args.address) elif args.solidity_file: # Compile Solidity source file(s) - #if args.graph and len(args.solidity_file) > 1: + # if args.graph and len(args.solidity_file) > 1: # exit_with_error(args.outform, # "Cannot generate call graphs from multiple input files. Please do it one at a time.") address, _ = mythril.load_from_solidity(args.solidity_file) # list of files @@ -226,5 +221,6 @@ def main(): except CriticalError as ce: exit_with_error(args.outform, str(ce)) + if __name__ == "__main__": main() diff --git a/mythril/mythril.py b/mythril/mythril.py index 831d8c5f..ba71f6df 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -97,6 +97,7 @@ class Mythril(object): raise CriticalError("Invalid JSON in signatures file " + self.sigs.signatures_file + "\n" + str(jde)) self.solc_binary = self._init_solc_binary(solv) + self.config_path = os.path.join(self.mythril_dir, 'config.ini') self.leveldb_dir = self._init_config() self.eth = None # ethereum API client @@ -119,39 +120,65 @@ class Mythril(object): def _init_config(self): - # If no config file exists, create it. Default LevelDB path is specified based on OS + # If no config file exists, create it and add default options. Default LevelDB path is specified based on OS - config_path = os.path.join(self.mythril_dir, 'config.ini') system = platform.system().lower() - fallback_dir = os.path.expanduser('~') + leveldb_fallback_dir = os.path.expanduser('~') if system.startswith("darwin"): - fallback_dir = os.path.join(fallback_dir, "Library", "Ethereum") + leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, "Library", "Ethereum") elif system.startswith("windows"): - fallback_dir = os.path.join(fallback_dir, "AppData", "Roaming", "Ethereum") + leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, "AppData", "Roaming", "Ethereum") else: - fallback_dir = os.path.join(fallback_dir, ".ethereum") - fallback_dir = os.path.join(fallback_dir, "geth", "chaindata") - - if not os.path.exists(config_path): - logging.info("No config file found. Creating default: " + config_path) - - config = ConfigParser(allow_no_value=True) - config.optionxform = str - config.add_section('defaults') - config.set('defaults', "#Default chaindata locations:") - config.set('defaults', "#– Mac: ~/Library/Ethereum/geth/chaindata") - config.set('defaults', "#– Linux: ~/.ethereum/geth/chaindata") - config.set('defaults', "#– Windows: %USERPROFILE%\\AppData\\Roaming\\Ethereum\\geth\\chaindata") - config.set('defaults', 'leveldb_dir', fallback_dir) - with codecs.open(config_path, 'w', 'utf-8') as fp: - config.write(fp) + leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, ".ethereum") + leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, "geth", "chaindata") + + if not os.path.exists(self.config_path): + logging.info("No config file found. Creating default: " + self.config_path) + open(self.config_path, 'a').close() config = ConfigParser(allow_no_value=True) config.optionxform = str - config.read(config_path, 'utf-8') - leveldb_dir = config.get('defaults', 'leveldb_dir', fallback=fallback_dir) + config.read(self.config_path, 'utf-8') + if 'defaults' not in config.sections() : + config = self._add_default_options(config) + + if not config.has_option('defaults', 'leveldb_dir'): + config = self._add_leveldb_option(config, leveldb_fallback_dir) + + if not config.has_option('defaults', 'dynamic_loading'): + config = self._add_dynamic_loading_option(config) + + with codecs.open(self.config_path, 'w', 'utf-8') as fp: + config.write(fp) + + config.read(self.config_path, 'utf-8') + leveldb_dir = config.get('defaults', 'leveldb_dir', fallback=leveldb_fallback_dir) return os.path.expanduser(leveldb_dir) + @staticmethod + def _add_default_options(config): + config.add_section('defaults') + return config + + @staticmethod + def _add_leveldb_option(config, leveldb_fallback_dir): + config.set('defaults', "#Default chaindata locations:") + config.set('defaults', "#– Mac: ~/Library/Ethereum/geth/chaindata") + config.set('defaults', "#– Linux: ~/.ethereum/geth/chaindata") + config.set('defaults', "#– Windows: %USERPROFILE%\\AppData\\Roaming\\Ethereum\\geth\\chaindata") + config.set('defaults', 'leveldb_dir', leveldb_fallback_dir) + return config + + @staticmethod + def _add_dynamic_loading_option(config): + config.set('defaults', '#– To connect to Infura use dynamic_loading: infura') + config.set('defaults', '#– To connect to Ipc use dynamic_loading: ipc') + config.set('defaults', '#– To connect to Rpc use ' + 'dynamic_loading: HOST:PORT / ganache / infura-[network_name]') + config.set('defaults', '#– To connect to local host use dynamic_loading: localhost') + config.set('defaults', 'dynamic_loading', 'infura') + return config + def analyze_truffle_project(self, *args, **kwargs): return analyze_truffle_project(*args, **kwargs) # just passthru for now @@ -226,6 +253,23 @@ class Mythril(object): self.eth = EthJsonRpc('localhost', 8545) logging.info("Using default RPC settings: http://localhost:8545") + def set_api_from_config_path(self): + config = ConfigParser(allow_no_value=False) + config.optionxform = str + config.read(self.config_path, 'utf-8') + if config.has_option('defaults', 'dynamic_loading'): + dynamic_loading = config.get('defaults', 'dynamic_loading') + else: + dynamic_loading = 'infura' + if dynamic_loading == 'ipc': + self.set_api_ipc() + elif dynamic_loading == 'infura': + self.set_api_rpc_infura() + elif dynamic_loading == 'localhost': + self.set_api_rpc_localhost() + else: + self.set_api_rpc(dynamic_loading) + def search_db(self, search): def search_callback(code_hash, code, addresses, balances): @@ -321,10 +365,7 @@ class Mythril(object): modules=None, verbose_report=False, max_depth=None, execution_timeout=None, ): - all_issues = [] - if self.dynld and self.eth is None: - self.set_api_rpc_infura() for contract in (contracts or self.contracts): sym = SymExecWrapper(contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, From 7daade31a0d2d69c494e36abc804c24f47cca229 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sat, 21 Jul 2018 15:53:31 +0530 Subject: [PATCH 02/15] Change relavent comment for config init --- mythril/mythril.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mythril/mythril.py b/mythril/mythril.py index ba71f6df..0969a5fc 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -119,8 +119,11 @@ class Mythril(object): return mythril_dir def _init_config(self): - - # If no config file exists, create it and add default options. Default LevelDB path is specified based on OS + """ + If no config file exists, create it and add default options. + Default LevelDB path is specified based on OS + dynamic loading is set to infura by default in the file + """ system = platform.system().lower() leveldb_fallback_dir = os.path.expanduser('~') From 9573ec161db710615aff79305c357cde5734fcbc Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sat, 21 Jul 2018 17:01:03 +0530 Subject: [PATCH 03/15] Remove self assignments --- mythril/ether/soliditycontract.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mythril/ether/soliditycontract.py b/mythril/ether/soliditycontract.py index f7c2778d..8577d73c 100644 --- a/mythril/ether/soliditycontract.py +++ b/mythril/ether/soliditycontract.py @@ -57,7 +57,6 @@ class SolidityContract(ETHContract): filename, _name = key.split(":") if filename == input_file and name == _name and len(contract['bin-runtime']): - name = name code = contract['bin-runtime'] creation_code = contract['bin'] srcmap = contract['srcmap-runtime'].split(";") @@ -71,7 +70,6 @@ class SolidityContract(ETHContract): filename, name = key.split(":") if filename == input_file and len(contract['bin-runtime']): - name = name code = contract['bin-runtime'] creation_code = contract['bin'] srcmap = contract['srcmap-runtime'].split(";") From 0c56c4fc8578924fa65b48c6f5423c0affd0db69 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sun, 22 Jul 2018 20:26:47 +0530 Subject: [PATCH 04/15] remove redundant returns and file opens in the functions --- mythril/mythril.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mythril/mythril.py b/mythril/mythril.py index 0969a5fc..01d58ae1 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -123,6 +123,7 @@ class Mythril(object): If no config file exists, create it and add default options. Default LevelDB path is specified based on OS dynamic loading is set to infura by default in the file + Returns: leveldb directory """ system = platform.system().lower() @@ -143,25 +144,23 @@ class Mythril(object): config.optionxform = str config.read(self.config_path, 'utf-8') if 'defaults' not in config.sections() : - config = self._add_default_options(config) + self._add_default_options(config) if not config.has_option('defaults', 'leveldb_dir'): - config = self._add_leveldb_option(config, leveldb_fallback_dir) + self._add_leveldb_option(config, leveldb_fallback_dir) if not config.has_option('defaults', 'dynamic_loading'): - config = self._add_dynamic_loading_option(config) + self._add_dynamic_loading_option(config) with codecs.open(self.config_path, 'w', 'utf-8') as fp: config.write(fp) - config.read(self.config_path, 'utf-8') leveldb_dir = config.get('defaults', 'leveldb_dir', fallback=leveldb_fallback_dir) return os.path.expanduser(leveldb_dir) @staticmethod def _add_default_options(config): config.add_section('defaults') - return config @staticmethod def _add_leveldb_option(config, leveldb_fallback_dir): @@ -170,7 +169,6 @@ class Mythril(object): config.set('defaults', "#– Linux: ~/.ethereum/geth/chaindata") config.set('defaults', "#– Windows: %USERPROFILE%\\AppData\\Roaming\\Ethereum\\geth\\chaindata") config.set('defaults', 'leveldb_dir', leveldb_fallback_dir) - return config @staticmethod def _add_dynamic_loading_option(config): @@ -180,7 +178,6 @@ class Mythril(object): 'dynamic_loading: HOST:PORT / ganache / infura-[network_name]') config.set('defaults', '#– To connect to local host use dynamic_loading: localhost') config.set('defaults', 'dynamic_loading', 'infura') - return config def analyze_truffle_project(self, *args, **kwargs): return analyze_truffle_project(*args, **kwargs) # just passthru for now From 1679f2f5e8709edaae710f719ea61ffad56ea6f1 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 24 Jul 2018 14:01:21 +0530 Subject: [PATCH 05/15] Allow dynamic loading for file based contracts --- mythril/interfaces/cli.py | 5 ----- mythril/mythril.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index d5a9566a..23cba251 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -91,11 +91,6 @@ def main(): print("Mythril version {}".format(VERSION)) sys.exit() - # -- args sanity checks -- - # Detect unsupported combinations of command line args - - if args.dynld and not args.address: - exit_with_error(args.outform, "Dynamic loader can be used in on-chain analysis mode only (-a).") # Parse cmdline args diff --git a/mythril/mythril.py b/mythril/mythril.py index 01d58ae1..21633943 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -143,7 +143,7 @@ class Mythril(object): config = ConfigParser(allow_no_value=True) config.optionxform = str config.read(self.config_path, 'utf-8') - if 'defaults' not in config.sections() : + if 'defaults' not in config.sections(): self._add_default_options(config) if not config.has_option('defaults', 'leveldb_dir'): From 4bec0826e24b491adbe073cdfe7560583f5de049 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 24 Jul 2018 17:08:49 +0530 Subject: [PATCH 06/15] Uncomment multiple file check for graphs code --- mythril/interfaces/cli.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index 23cba251..d9b52aec 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -91,7 +91,6 @@ def main(): print("Mythril version {}".format(VERSION)) sys.exit() - # Parse cmdline args if not (args.search or args.hash or args.disassemble or args.graph or args.fire_lasers @@ -159,9 +158,9 @@ def main(): address, _ = mythril.load_from_address(args.address) elif args.solidity_file: # Compile Solidity source file(s) - # if args.graph and len(args.solidity_file) > 1: - # exit_with_error(args.outform, - # "Cannot generate call graphs from multiple input files. Please do it one at a time.") + if args.graph and len(args.solidity_file) > 1: + exit_with_error(args.outform, + "Cannot generate call graphs from multiple input files. Please do it one at a time.") address, _ = mythril.load_from_solidity(args.solidity_file) # list of files else: exit_with_error(args.outform, From d69004bd6163d57cf4e1b2cbb4621b4f0465bb07 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 24 Jul 2018 17:37:32 +0530 Subject: [PATCH 07/15] Default to config for addresses if nothing is mentioned --- mythril/interfaces/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index d9b52aec..23341a03 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -127,7 +127,7 @@ def main(): mythril.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls) elif args.ipc: mythril.set_api_ipc() - else: + elif not args.dynld: mythril.set_api_rpc_localhost() elif args.leveldb or args.search: # Open LevelDB if necessary From 143ec422e9bd7ae28afca0a83f20c3385da5aed2 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 25 Jul 2018 22:44:24 +0530 Subject: [PATCH 08/15] Use solidity filename for truffle --- mythril/support/truffle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index 0416c27c..cc70dea6 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -31,6 +31,7 @@ def analyze_truffle_project(args): try: name = contractdata['contractName'] bytecode = contractdata['deployedBytecode'] + filename = contractdata['sourcePath'].split('/')[-1] except: print("Unable to parse contract data. Please use Truffle 4 to compile your project.") sys.exit() From a1b45f803ebb76666cc7939f4b6552e4c2025258 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 25 Jul 2018 23:43:21 +0530 Subject: [PATCH 09/15] Use pathlib over split --- mythril/support/truffle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index cc70dea6..d4545b62 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -1,4 +1,5 @@ import os +from pathlib import Path import re import sys import json @@ -31,7 +32,7 @@ def analyze_truffle_project(args): try: name = contractdata['contractName'] bytecode = contractdata['deployedBytecode'] - filename = contractdata['sourcePath'].split('/')[-1] + filename = Path(contractdata['sourcePath']).name except: print("Unable to parse contract data. Please use Truffle 4 to compile your project.") sys.exit() From 7fc3396d36450d99eb7d2e4600c59f7dd6132165 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 25 Jul 2018 23:55:29 +0530 Subject: [PATCH 10/15] Support windows Path --- mythril/support/truffle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index d4545b62..f623ba8f 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -1,5 +1,5 @@ import os -from pathlib import Path +from pathlib import PurePath import re import sys import json @@ -32,7 +32,7 @@ def analyze_truffle_project(args): try: name = contractdata['contractName'] bytecode = contractdata['deployedBytecode'] - filename = Path(contractdata['sourcePath']).name + filename = PurePath(contractdata['sourcePath']).name except: print("Unable to parse contract data. Please use Truffle 4 to compile your project.") sys.exit() From b7f86f13f84e723fe473d3a8f85408427d57380f Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 26 Jul 2018 16:13:18 +0530 Subject: [PATCH 11/15] Use bytes encoding to make the mapping --- mythril/ether/soliditycontract.py | 4 ++-- mythril/support/truffle.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mythril/ether/soliditycontract.py b/mythril/ether/soliditycontract.py index 8577d73c..7e544800 100644 --- a/mythril/ether/soliditycontract.py +++ b/mythril/ether/soliditycontract.py @@ -3,6 +3,7 @@ from mythril.ether.ethcontract import ETHContract from mythril.ether.util import * from mythril.exceptions import NoContractFoundError + class SourceMapping: def __init__(self, solidity_file_idx, offset, length, lineno): @@ -91,8 +92,7 @@ class SolidityContract(ETHContract): if len(mapping) > 2 and len(mapping[2]) > 0: idx = int(mapping[2]) - - lineno = self.solidity_files[idx].data[0:offset].count('\n') + 1 + lineno = self.solidity_files[idx].data.encode('utf-8')[0:offset].count('\n'.encode('utf-8')) + 1 self.mappings.append(SourceMapping(idx, offset, length, lineno)) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index f623ba8f..140df386 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -76,7 +76,7 @@ def analyze_truffle_project(args): if len(mapping) > 2 and len(mapping[2]) > 0: idx = int(mapping[2]) - lineno = source[0:offset].count('\n') + 1 + lineno = source.encode('utf-8')[0:offset].count('\n'.encode('utf-8')) + 1 mappings.append(SourceMapping(idx, offset, length, lineno)) From 29f89864d0061b13336c8af7a880b004e67deade Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 26 Jul 2018 17:07:14 +0530 Subject: [PATCH 12/15] Fix the code lines mapping --- mythril/ether/soliditycontract.py | 2 +- mythril/support/truffle.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mythril/ether/soliditycontract.py b/mythril/ether/soliditycontract.py index 7e544800..f5825d0e 100644 --- a/mythril/ether/soliditycontract.py +++ b/mythril/ether/soliditycontract.py @@ -109,7 +109,7 @@ class SolidityContract(ETHContract): offset = self.mappings[index].offset length = self.mappings[index].length - code = solidity_file.data[offset:offset + length] + code = solidity_file.data.encode('utf-8')[offset:offset + length].decode('utf-8') lineno = self.mappings[index].lineno return SourceCodeInfo(filename, lineno, code) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index 140df386..d59879ab 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -90,7 +90,7 @@ def analyze_truffle_project(args): length = mappings[index].length issue.filename = filename - issue.code = source[offset:offset + length] + issue.code = source.encode('utf-8')[offset:offset + length].decode('utf-8') issue.lineno = mappings[index].lineno except IndexError: logging.debug("No code mapping at index %d", index) From b7505e29824e9876643331951882665ff323f961 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 27 Jul 2018 00:40:57 +0530 Subject: [PATCH 13/15] Send a list for signatures --- mythril/support/signatures.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index e1582b68..ae432af8 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -154,8 +154,9 @@ class SignatureDb(object): """ if not sighash.startswith("0x"): sighash = "0x%s" % sighash # normalize sighash format - - if self.enable_online_lookup and not self.signatures.get(sighash) and sighash not in self.online_lookup_miss and time.time() > self.online_directory_unavailable_until: + if self.signatures.get(sighash): + return [self.signatures[sighash]] + if self.enable_online_lookup and sighash not in self.online_lookup_miss and time.time() > self.online_directory_unavailable_until: # online lookup enabled, and signature not in cache, sighash was not a miss earlier, and online directory not down logging.debug("Signatures: performing online lookup for sighash %r" % sighash) try: From 526ebd14ace138852068c44be16fb126f5c85072 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 27 Jul 2018 12:30:55 +0530 Subject: [PATCH 14/15] Fix tests for report --- mythril/support/signatures.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index e1582b68..ac62a2e1 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -154,7 +154,6 @@ class SignatureDb(object): """ if not sighash.startswith("0x"): sighash = "0x%s" % sighash # normalize sighash format - if self.enable_online_lookup and not self.signatures.get(sighash) and sighash not in self.online_lookup_miss and time.time() > self.online_directory_unavailable_until: # online lookup enabled, and signature not in cache, sighash was not a miss earlier, and online directory not down logging.debug("Signatures: performing online lookup for sighash %r" % sighash) @@ -169,8 +168,11 @@ class SignatureDb(object): except FourByteDirectoryOnlineLookupError as fbdole: self.online_directory_unavailable_until = time.time() + 2 * 60 # wait at least 2 mins to try again logging.warning("online function signature lookup not available. will not try to lookup hash for the next 2 minutes. exception: %r" % fbdole) + if type(self.signatures[sighash]) != list: + return [self.signatures[sighash]] return self.signatures[sighash] # raise keyerror + def __getitem__(self, item): """ Provide dict interface Signatures()[sighash] From 75cc79df5466b39078464efe875723b12043137b Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 27 Jul 2018 17:30:46 +0530 Subject: [PATCH 15/15] Allow frequent write to disk --- mythril/mythril.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mythril/mythril.py b/mythril/mythril.py index 1c11d75d..e687e480 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -332,6 +332,9 @@ class Mythril(object): # import signatures from solidity source with open(file, encoding="utf-8") as f: self.sigs.import_from_solidity_source(f.read()) + # Save updated function signatures + self.sigs.write() # dump signatures to disk (previously opened file or default location) + if contract_name is not None: contract = SolidityContract(file, contract_name, solc_args=self.solc_args) self.contracts.append(contract) @@ -341,6 +344,7 @@ class Mythril(object): self.contracts.append(contract) contracts.append(contract) + except FileNotFoundError: raise CriticalError("Input file not found: " + file) except CompilerError as e: @@ -348,8 +352,6 @@ class Mythril(object): except NoContractFoundError: logging.info("The file " + file + " does not contain a compilable contract.") - # Save updated function signatures - self.sigs.write() # dump signatures to disk (previously opened file or default location) return address, contracts