From 5d44a957e165f2250d1f6d4a49fd56a004b463df Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Sun, 25 Jun 2023 20:35:04 -0700 Subject: [PATCH 01/12] wip: use forge directly --- src/halmos/__main__.py | 149 ++++++++++++++++++++++++++++++++++------- tests/test_cli.py | 2 +- 2 files changed, 124 insertions(+), 27 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 32944d5c..d7bdf401 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -8,6 +8,7 @@ import argparse import re import traceback +import shutil from crytic_compile import cryticparser from crytic_compile import CryticCompile, InvalidCompilation @@ -310,7 +311,7 @@ def setup( setup_sig, setup_name, setup_selector = setup_info.sig, setup_info.name, setup_info.selector if setup_sig: - wstore(setup_ex.calldata, 0, 4, BitVecVal(setup_selector, 32)) + wstore(setup_ex.calldata, 0, 4, BitVecVal(int(setup_selector, 16), 32)) dyn_param_size = [] # TODO: propagate to run mk_calldata(abi, setup_info, setup_ex.calldata, dyn_param_size, args) @@ -379,7 +380,7 @@ def run( cd = [] - wstore(cd, 0, 4, BitVecVal(funselector, 32)) + wstore(cd, 0, 4, BitVecVal(int(funselector, 16), 32)) dyn_param_size = [] mk_calldata(abi, fun_info, cd, dyn_param_size, args) @@ -819,18 +820,89 @@ def main() -> int: # compile # - try: - crytic_compile_args, crytic_compile_unknown_args = crytic_compile_parser.parse_known_args() - - both_unknown = set(halmos_unknown_args) & set(crytic_compile_unknown_args) - if both_unknown: - print(color_warn(f'error: unrecognized arguments: {" ".join(both_unknown)}')) +# try: +# crytic_compile_args, crytic_compile_unknown_args = crytic_compile_parser.parse_known_args() + +# both_unknown = set(halmos_unknown_args) & set(crytic_compile_unknown_args) +# if both_unknown: +# print(color_warn(f'error: unrecognized arguments: {" ".join(both_unknown)}')) +# return 1 + +# cryticCompile = CryticCompile(target=args.root, **vars(crytic_compile_args)) +# except InvalidCompilation as e: +# print(color_warn(f'Parse error: {e}')) +# return 1 + + cmd = [ + "forge", + "build", + '--extra-output', + 'storageLayout', + 'metadata' + ] + + with subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=args.root, + executable=shutil.which(cmd[0]), + ) as process: + + stdout_bytes, stderr_bytes = process.communicate() + stdout, stderr = ( + stdout_bytes.decode(errors="backslashreplace"), + stderr_bytes.decode(errors="backslashreplace"), + ) # convert bytestrings to unicode strings + + # print(stdout) + if stderr: + print(color_warn(f'Parse error: {stderr}')) return 1 - cryticCompile = CryticCompile(target=args.root, **vars(crytic_compile_args)) - except InvalidCompilation as e: - print(color_warn(f'Parse error: {e}')) - return 1 + src_ids = {} # compiler version -> id -> abspath + + contract_json = {} # compiler version -> filename -> contract name -> (json, type) + + out_path = os.path.join(args.root, 'out') + for sol_dirname in os.listdir(out_path): + if sol_dirname.endswith('.sol'): + sol_path = os.path.join(out_path, sol_dirname) + if os.path.isdir(sol_path): + for json_filename in os.listdir(sol_path): + # print(sol_dirname, json_filename) + if json_filename.startswith('.'): continue + if not json_filename.endswith('.json'): continue + + json_path = os.path.join(sol_path, json_filename) + with open(json_path, encoding='utf8') as f: + json_out = json.load(f) + + compiler_version = json_out['metadata']['compiler']['version'] + if compiler_version not in src_ids: + src_ids[compiler_version] = {} + _src_ids = src_ids[compiler_version] + if compiler_version not in contract_json: + contract_json[compiler_version] = {} + if sol_dirname not in contract_json[compiler_version]: + contract_json[compiler_version][sol_dirname] = {} + _contract_json = contract_json[compiler_version][sol_dirname] + + src_id = json_out['id'] + abspath = json_out['ast']['absolutePath'] + if src_id in _src_ids and _src_ids[src_id] != abspath: raise ValueError(src_id, _src_ids[src_id], abspath) + _src_ids[src_id] = abspath + + contract_name = json_filename.split('.')[0] + + for node in json_out['ast']['nodes']: + if node['nodeType'] == 'ContractDefinition' and node['name'] == contract_name: + abstract = 'abstract ' if node.get('abstract') else '' + contract_type = abstract + node['contractKind'] + break + + if contract_name in _contract_json: raise ValueError(contract_name) + _contract_json[contract_name] = (json_out, contract_type) main_mid = timer() @@ -842,30 +914,55 @@ def main() -> int: total_failed = 0 total_found = 0 - for compilation_id, compilation_unit in cryticCompile.compilation_units.items(): +# for compilation_id, compilation_unit in cryticCompile.compilation_units.items(): - for filename in sorted(compilation_unit.filenames): - contracts_names = compilation_unit.filename_to_contracts[filename] - source_unit = compilation_unit.source_units[filename] +# for filename in sorted(compilation_unit.filenames): +# contracts_names = compilation_unit.filename_to_contracts[filename] +# source_unit = compilation_unit.source_units[filename] - if args.contract: - if args.contract not in contracts_names: continue - contracts = [args.contract] - else: - contracts = sorted(contracts_names) +# if args.contract: +# if args.contract not in contracts_names: continue +# contracts = [args.contract] +# else: +# contracts = sorted(contracts_names) + +# for contract in contracts: +# contract_start = timer() + +# hexcode = source_unit.bytecodes_runtime[contract] +# abi = source_unit.abis[contract] +# methodIdentifiers = source_unit.hashes(contract) + +# funsigs = [funsig for funsig in methodIdentifiers if funsig.startswith(args.function)] +# total_found += len(funsigs) + +# if funsigs: +# num_passed = 0 +# num_failed = 0 +# print(f'\nRunning {len(funsigs)} tests for {filename.short}:{contract}') + + for compiler_version in sorted(contract_json): + contract_json_compiler = contract_json[compiler_version] + for filename in sorted(contract_json_compiler): + for cname in sorted(contract_json_compiler[filename]): + + if args.contract and args.contract != cname: continue + + (out, contract_type) = contract_json_compiler[filename][cname] + + if contract_type != 'contract': continue - for contract in contracts: contract_start = timer() - hexcode = source_unit.bytecodes_runtime[contract] - abi = source_unit.abis[contract] - methodIdentifiers = source_unit.hashes(contract) + hexcode = out['deployedBytecode']['object'] + abi = out['abi'] + methodIdentifiers = out['methodIdentifiers'] funsigs = [funsig for funsig in methodIdentifiers if funsig.startswith(args.function)] if funsigs: total_found += len(funsigs) - print(f'\nRunning {len(funsigs)} tests for {filename.short}:{contract}') + print(f"\nRunning {len(funsigs)} tests for {out['ast']['absolutePath']}:{cname}") run_args = RunArgs(funsigs, hexcode, abi, methodIdentifiers) enable_parallel = args.test_parallel and len(funsigs) > 1 diff --git a/tests/test_cli.py b/tests/test_cli.py index 2ce38c10..8514310e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -36,7 +36,7 @@ def setup_sig(): @pytest.fixture def setup_selector(): - return int('0a9254e4', 16) + return '0a9254e4' def test_decode_concrete_bytecode(): From e6dbeacc21639932558e6f5d0336634ff1080436 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Thu, 13 Jul 2023 23:29:48 -0700 Subject: [PATCH 02/12] update forge build --- src/halmos/__main__.py | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index d7bdf401..9d74e6e8 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -8,7 +8,6 @@ import argparse import re import traceback -import shutil from crytic_compile import cryticparser from crytic_compile import CryticCompile, InvalidCompilation @@ -833,32 +832,17 @@ def main() -> int: # print(color_warn(f'Parse error: {e}')) # return 1 - cmd = [ - "forge", - "build", - '--extra-output', - 'storageLayout', - 'metadata' + forge_cmd = [ + 'forge', 'build', # shutil.which('forge') + '--root', args.root, + '--extra-output', 'storageLayout', 'metadata' ] - with subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=args.root, - executable=shutil.which(cmd[0]), - ) as process: - - stdout_bytes, stderr_bytes = process.communicate() - stdout, stderr = ( - stdout_bytes.decode(errors="backslashreplace"), - stderr_bytes.decode(errors="backslashreplace"), - ) # convert bytestrings to unicode strings - - # print(stdout) - if stderr: - print(color_warn(f'Parse error: {stderr}')) - return 1 + forge_exitcode = subprocess.run(forge_cmd).returncode + + if forge_exitcode: + print(color_warn(f'build failed: {forge_cmd}')) + return 1 src_ids = {} # compiler version -> id -> abspath From 7a1a2c4e4de5ef4c041a4a744b34ddd975449eb8 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 11:30:34 -0700 Subject: [PATCH 03/12] remove crytic-compile parsing options --- src/halmos/__main__.py | 45 ++---------------------------------------- tests/test_fixtures.py | 2 +- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 9d74e6e8..25b16eb1 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -29,24 +29,6 @@ if hasattr(sys, 'set_int_max_str_digits'): # Python version >=3.8.14, >=3.9.14, >=3.10.7, or >=3.11 sys.set_int_max_str_digits(0) -def mk_crytic_parser() -> argparse.ArgumentParser: - crytic_compile_parser = argparse.ArgumentParser() - cryticparser.init(crytic_compile_parser) - return crytic_compile_parser - -def print_help_compile(crytic_compile_parser: argparse.ArgumentParser) -> None: - formatter = crytic_compile_parser._get_formatter() - for action_group in crytic_compile_parser._action_groups: - if action_group.title == 'options': - # prints "--help", which is not helpful - continue - - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - crytic_compile_parser._print_message(formatter.format_help()) - def parse_args(args=None) -> argparse.Namespace: parser = argparse.ArgumentParser(prog='halmos', epilog='For more information, see https://github.com/a16z/halmos') @@ -78,11 +60,6 @@ def parse_args(args=None) -> argparse.Namespace: group_debug.add_argument('--print-setup-states', action='store_true', help='print setup execution states') group_debug.add_argument('--print-full-model', action='store_true', help='print full counterexample model') - # build options - group_build = parser.add_argument_group("Build options") - - group_build.add_argument('--help-compile', action='store_true', help='print build options (foundry, hardhat, etc.)') - # smt solver options group_solver = parser.add_argument_group("Solver options") @@ -114,7 +91,7 @@ def parse_args(args=None) -> argparse.Namespace: group_experimental.add_argument('--symbolic-jump', action='store_true', help='support symbolic jump destination') group_experimental.add_argument('--print-potential-counterexample', action='store_true', help='print potentially invalid counterexamples') - return parser.parse_known_args(args) + return parser.parse_args(args) @dataclass(frozen=True) class FunctionInfo: @@ -799,12 +776,7 @@ def main() -> int: # global args - args, halmos_unknown_args = parse_args() - - crytic_compile_parser = mk_crytic_parser() - if args.help_compile: - print_help_compile(crytic_compile_parser) - return 0 + args = parse_args() if args.version: print(f"Halmos {metadata.version('halmos')}") @@ -819,19 +791,6 @@ def main() -> int: # compile # -# try: -# crytic_compile_args, crytic_compile_unknown_args = crytic_compile_parser.parse_known_args() - -# both_unknown = set(halmos_unknown_args) & set(crytic_compile_unknown_args) -# if both_unknown: -# print(color_warn(f'error: unrecognized arguments: {" ".join(both_unknown)}')) -# return 1 - -# cryticCompile = CryticCompile(target=args.root, **vars(crytic_compile_args)) -# except InvalidCompilation as e: -# print(color_warn(f'Parse error: {e}')) -# return 1 - forge_cmd = [ 'forge', 'build', # shutil.which('forge') '--root', args.root, diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index f320324d..2a33e014 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -7,7 +7,7 @@ @pytest.fixture def args(): - (args, unknown_args) = parse_args([]) + args = parse_args([]) # set the global args for the main module halmos.__main__.args = args From 07a4885691f5a62c1655ff2ff1c051943462a910 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 13:12:21 -0700 Subject: [PATCH 04/12] use forge build --- src/halmos/__main__.py | 159 ++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 89 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 25b16eb1..8b295a7d 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -60,6 +60,11 @@ def parse_args(args=None) -> argparse.Namespace: group_debug.add_argument('--print-setup-states', action='store_true', help='print setup execution states') group_debug.add_argument('--print-full-model', action='store_true', help='print full counterexample model') + # build options + group_build = parser.add_argument_group("Build options") + + group_build.add_argument('--forge-build-out', metavar='DIRECTORY_NAME', default='out', help='forge build artifacts directory name (default: %(default)s)') + # smt solver options group_solver = parser.add_argument_group("Solver options") @@ -760,6 +765,49 @@ def mk_arrlen(args: argparse.Namespace) -> Dict[str, int]: return arrlen +def parse_build_out(args: argparse.Namespace) -> Dict: + result = {} # compiler version -> source filename -> contract name -> (json, type) + + out_path = os.path.join(args.root, args.forge_build_out) + if not os.path.exists(out_path): raise ValueError('not exist', out_path) + + for sol_dirname in os.listdir(out_path): # for each source filename + if not sol_dirname.endswith('.sol'): continue + + sol_path = os.path.join(out_path, sol_dirname) + if not os.path.isdir(sol_path): continue + + for json_filename in os.listdir(sol_path): # for each contract name + if not json_filename.endswith('.json'): continue + if json_filename.startswith('.'): continue + + json_path = os.path.join(sol_path, json_filename) + with open(json_path, encoding='utf8') as f: + json_out = json.load(f) + + compiler_version = json_out['metadata']['compiler']['version'] + if compiler_version not in result: + result[compiler_version] = {} + if sol_dirname not in result[compiler_version]: + result[compiler_version][sol_dirname] = {} + contract_map = result[compiler_version][sol_dirname] + + contract_name = json_filename.split('.')[0] # cut off compiler version number as well + + contract_type = None + for node in json_out['ast']['nodes']: + if node['nodeType'] == 'ContractDefinition' and node['name'] == contract_name: + abstract = 'abstract ' if node.get('abstract') else '' + contract_type = abstract + node['contractKind'] + break + if contract_type is None: raise ValueError('no contract type', contract_name) + + if contract_name in contract_map: raise ValueError('duplicate contract names in the same file', contract_name, sol_dirname) + contract_map[contract_name] = (json_out, contract_type) + + return result + + def main() -> int: main_start = timer() @@ -791,61 +839,24 @@ def main() -> int: # compile # - forge_cmd = [ - 'forge', 'build', # shutil.which('forge') + build_cmd = [ + 'forge', # shutil.which('forge') + 'build', '--root', args.root, '--extra-output', 'storageLayout', 'metadata' ] - forge_exitcode = subprocess.run(forge_cmd).returncode + # run forge without capturing stdout/stderr + build_exitcode = subprocess.run(build_cmd).returncode - if forge_exitcode: - print(color_warn(f'build failed: {forge_cmd}')) + if build_exitcode: + print(color_warn(f'build failed: {build_cmd}')) return 1 - src_ids = {} # compiler version -> id -> abspath - - contract_json = {} # compiler version -> filename -> contract name -> (json, type) - - out_path = os.path.join(args.root, 'out') - for sol_dirname in os.listdir(out_path): - if sol_dirname.endswith('.sol'): - sol_path = os.path.join(out_path, sol_dirname) - if os.path.isdir(sol_path): - for json_filename in os.listdir(sol_path): - # print(sol_dirname, json_filename) - if json_filename.startswith('.'): continue - if not json_filename.endswith('.json'): continue - - json_path = os.path.join(sol_path, json_filename) - with open(json_path, encoding='utf8') as f: - json_out = json.load(f) - - compiler_version = json_out['metadata']['compiler']['version'] - if compiler_version not in src_ids: - src_ids[compiler_version] = {} - _src_ids = src_ids[compiler_version] - if compiler_version not in contract_json: - contract_json[compiler_version] = {} - if sol_dirname not in contract_json[compiler_version]: - contract_json[compiler_version][sol_dirname] = {} - _contract_json = contract_json[compiler_version][sol_dirname] - - src_id = json_out['id'] - abspath = json_out['ast']['absolutePath'] - if src_id in _src_ids and _src_ids[src_id] != abspath: raise ValueError(src_id, _src_ids[src_id], abspath) - _src_ids[src_id] = abspath - - contract_name = json_filename.split('.')[0] - - for node in json_out['ast']['nodes']: - if node['nodeType'] == 'ContractDefinition' and node['name'] == contract_name: - abstract = 'abstract ' if node.get('abstract') else '' - contract_type = abstract + node['contractKind'] - break - - if contract_name in _contract_json: raise ValueError(contract_name) - _contract_json[contract_name] = (json_out, contract_type) + try: + build_out = parse_build_out(args) + except Error as err: + print(color_warn(f'build output parsing failed: {err}')) main_mid = timer() @@ -857,55 +868,25 @@ def main() -> int: total_failed = 0 total_found = 0 -# for compilation_id, compilation_unit in cryticCompile.compilation_units.items(): - -# for filename in sorted(compilation_unit.filenames): -# contracts_names = compilation_unit.filename_to_contracts[filename] -# source_unit = compilation_unit.source_units[filename] - -# if args.contract: -# if args.contract not in contracts_names: continue -# contracts = [args.contract] -# else: -# contracts = sorted(contracts_names) - -# for contract in contracts: -# contract_start = timer() - -# hexcode = source_unit.bytecodes_runtime[contract] -# abi = source_unit.abis[contract] -# methodIdentifiers = source_unit.hashes(contract) - -# funsigs = [funsig for funsig in methodIdentifiers if funsig.startswith(args.function)] -# total_found += len(funsigs) - -# if funsigs: -# num_passed = 0 -# num_failed = 0 -# print(f'\nRunning {len(funsigs)} tests for {filename.short}:{contract}') - - for compiler_version in sorted(contract_json): - contract_json_compiler = contract_json[compiler_version] - for filename in sorted(contract_json_compiler): - for cname in sorted(contract_json_compiler[filename]): - - if args.contract and args.contract != cname: continue - - (out, contract_type) = contract_json_compiler[filename][cname] + for compiler_version in sorted(build_out): + build_out_map = build_out[compiler_version] + for filename in sorted(build_out_map): + for contract_name in sorted(build_out_map[filename]): + if args.contract and args.contract != contract_name: continue + (contract_json, contract_type) = build_out_map[filename][contract_name] if contract_type != 'contract': continue - contract_start = timer() - - hexcode = out['deployedBytecode']['object'] - abi = out['abi'] - methodIdentifiers = out['methodIdentifiers'] + hexcode = contract_json['deployedBytecode']['object'] + abi = contract_json['abi'] + methodIdentifiers = contract_json['methodIdentifiers'] funsigs = [funsig for funsig in methodIdentifiers if funsig.startswith(args.function)] if funsigs: total_found += len(funsigs) - print(f"\nRunning {len(funsigs)} tests for {out['ast']['absolutePath']}:{cname}") + print(f"\nRunning {len(funsigs)} tests for {contract_json['ast']['absolutePath']}:{contract_name}") + contract_start = timer() run_args = RunArgs(funsigs, hexcode, abi, methodIdentifiers) enable_parallel = args.test_parallel and len(funsigs) > 1 From e17391ece1ee2a01cc4a590a67ca96ac8ea2cfd2 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 13:29:20 -0700 Subject: [PATCH 05/12] remove crytic-compile dependency --- pyproject.toml | 1 - requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fec6e17f..fa7ba608 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,6 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = [ - "crytic-compile >= 0.3.0, < 0.3.2", "z3-solver", ] dynamic = ["version"] diff --git a/requirements.txt b/requirements.txt index 248828a7..c91c87dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -crytic-compile >= 0.3.0, < 0.3.2 z3-solver From d59227f65210a968317b2823877371bbc6ef0675 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 13:33:37 -0700 Subject: [PATCH 06/12] remove crytic-compile imports --- src/halmos/__main__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 8b295a7d..7b64251b 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -9,8 +9,6 @@ import re import traceback -from crytic_compile import cryticparser -from crytic_compile import CryticCompile, InvalidCompilation from dataclasses import dataclass from timeit import default_timer as timer from importlib import metadata From de842672842541b6b51daf3606cbf861c60dfdf8 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 13:44:59 -0700 Subject: [PATCH 07/12] remove crytic-compile bug mitigation --- src/halmos/__main__.py | 3 +-- tests/test_cli.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 7b64251b..3c1f4616 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -109,8 +109,7 @@ def str_tuple(args: List) -> str: for arg in args: typ = arg['type'] if typ == 'tuple': - # ret.append(str_tuple(arg['components'])) - ret.append(typ) # crytic-compile bug + ret.append(str_tuple(arg['components'])) else: ret.append(typ) return '(' + ','.join(ret) + ')' diff --git a/tests/test_cli.py b/tests/test_cli.py index 8514310e..4d483364 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -227,9 +227,7 @@ def test_decode(): "type": "function" } """), - # TODO: fix crytic-compile bug - # 'fooStruct(((uint256,uint256),uint256),uint256)' - ('fooStruct(tuple,uint256)', """ + ('fooStruct(((uint256,uint256),uint256),uint256)', """ { "inputs": [ { From 180ed4eedd3cf295098c58daca67ea559ae692dd Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 14:58:31 -0700 Subject: [PATCH 08/12] fix typo --- src/halmos/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 3c1f4616..bbfb8922 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -852,7 +852,7 @@ def main() -> int: try: build_out = parse_build_out(args) - except Error as err: + except Exception as err: print(color_warn(f'build output parsing failed: {err}')) main_mid = timer() From 16eb5842ecc039ff7dda44ebce438ab36fe5ff2c Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 15:21:08 -0700 Subject: [PATCH 09/12] add logging.basicConfig() --- src/halmos/warnings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/halmos/warnings.py b/src/halmos/warnings.py index f271e05c..ccdce106 100644 --- a/src/halmos/warnings.py +++ b/src/halmos/warnings.py @@ -5,6 +5,7 @@ from .utils import color_warn logger = logging.getLogger(__name__) +logging.basicConfig() WARNINGS_BASE_URL = 'https://github.com/a16z/halmos/wiki/warnings' From 7ef9e7a4d71e242a085d533d326d1309bec000a1 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 15:27:44 -0700 Subject: [PATCH 10/12] fix error handling --- src/halmos/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index bbfb8922..3ada85b5 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -766,7 +766,7 @@ def parse_build_out(args: argparse.Namespace) -> Dict: result = {} # compiler version -> source filename -> contract name -> (json, type) out_path = os.path.join(args.root, args.forge_build_out) - if not os.path.exists(out_path): raise ValueError('not exist', out_path) + if not os.path.exists(out_path): raise FileNotFoundError(f'{out_path} does not exist') for sol_dirname in os.listdir(out_path): # for each source filename if not sol_dirname.endswith('.sol'): continue @@ -854,6 +854,7 @@ def main() -> int: build_out = parse_build_out(args) except Exception as err: print(color_warn(f'build output parsing failed: {err}')) + return 1 main_mid = timer() From 6ef4ea01364ad53b29aec5db0ef4f04651b83fb7 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 15:33:28 -0700 Subject: [PATCH 11/12] better error msg --- src/halmos/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/halmos/__main__.py b/src/halmos/__main__.py index 3ada85b5..354039f7 100644 --- a/src/halmos/__main__.py +++ b/src/halmos/__main__.py @@ -766,7 +766,7 @@ def parse_build_out(args: argparse.Namespace) -> Dict: result = {} # compiler version -> source filename -> contract name -> (json, type) out_path = os.path.join(args.root, args.forge_build_out) - if not os.path.exists(out_path): raise FileNotFoundError(f'{out_path} does not exist') + if not os.path.exists(out_path): raise FileNotFoundError(f'the build output directory `{out_path}` does not exist') for sol_dirname in os.listdir(out_path): # for each source filename if not sol_dirname.endswith('.sol'): continue From 3fd54e9412634010d280fb9c625f26b1e8d14788 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Fri, 14 Jul 2023 15:42:22 -0700 Subject: [PATCH 12/12] update logging format --- src/halmos/warnings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/halmos/warnings.py b/src/halmos/warnings.py index ccdce106..93680c1f 100644 --- a/src/halmos/warnings.py +++ b/src/halmos/warnings.py @@ -4,7 +4,7 @@ from .utils import color_warn -logger = logging.getLogger(__name__) +logger = logging.getLogger('Halmos') logging.basicConfig() WARNINGS_BASE_URL = 'https://github.com/a16z/halmos/wiki/warnings'