From 30cde9f15c9b4f0d3a808a5ad8ecb174cf182c98 Mon Sep 17 00:00:00 2001 From: Pablo Gordillo Date: Fri, 21 Feb 2025 09:00:32 +0100 Subject: [PATCH 1/3] adding support for importing from asm_json --- src/execution/main_execution.py | 12 +++-- src/execution/sol_compilation.py | 24 ++++++++++ .../reconstruct_bytecode.py | 47 +++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/execution/main_execution.py b/src/execution/main_execution.py index b30f178a..12ff0f6c 100644 --- a/src/execution/main_execution.py +++ b/src/execution/main_execution.py @@ -9,7 +9,7 @@ from parser.parser import parse_CFG_from_json_dict from parser.cfg import CFG from execution.sol_compilation import SolidityCompilation -from solution_generation.reconstruct_bytecode import asm_from_cfg, store_asm_output, store_binary_output +from solution_generation.reconstruct_bytecode import asm_from_cfg, store_asm_output, store_binary_output, store_asm_standard_json_output from greedy.ids_from_spec import cfg_spec_ids from liveness.layout_generation import layout_generation from cfg_methods.preprocessing_methods import preprocess_cfg @@ -114,7 +114,11 @@ def main(args): assembly_path = store_asm_output(asm_contract, cfg_name, cfg_dir) - synt_binary = SolidityCompilation.importer_assembly_file(assembly_path, solc_executable=args.solc_executable) - print("Contract: " + cfg_name + " -> EVM Code: " + synt_binary) + std_assembly_path = store_asm_standard_json_output(asm_contract, cfg_name, cfg_dir) + + #synt_binary = SolidityCompilation.importer_assembly_file(assembly_path, solc_executable=args.solc_executable) + synt_binary_stdjson = SolidityCompilation.importer_assembly_standard_json_file(std_assembly_path, deployed_contract = cfg_name, solc_executable = args.solc_executable) + + print("Contract: " + cfg_name + " -> EVM Code: " + synt_binary_stdjson) - store_binary_output(cfg_name, synt_binary, cfg_dir) + store_binary_output(cfg_name, synt_binary_stdjson, cfg_dir) diff --git a/src/execution/sol_compilation.py b/src/execution/sol_compilation.py index 5a4234d8..f9ea9056 100644 --- a/src/execution/sol_compilation.py +++ b/src/execution/sol_compilation.py @@ -225,6 +225,22 @@ def process_importer_output(sol_output: str, deployed_contract: Optional[str], s contract_header = r"Binary:\n(.*?)\n" return re.search(contract_header, sol_output).group(1) +def process_importer_standard_output(sol_output: str, deployed_contract: Optional[str], selected_header: str) -> str: + """ + Given the compiler output from solc after the importer standard-json option, extracts the information + of the corresponding contract (binary) + """ + + json_output = json.loads(sol_output) + + contracts = json_output["contracts"] + assert deployed_contract in contracts, f"Contract {deployed_contract} not deployed" + + deployed_contract_info = contracts[deployed_contract][""] + + return deployed_contract_info["evm"]["bytecode"]["object"] + + class SolidityCompilation: """ @@ -264,6 +280,14 @@ def importer_assembly_file(json_file: str, deployed_contract: Optional[str] = No compilation.process_output_function = process_importer_output return compilation.compile_single_sol_file(json_file, deployed_contract, "bin") + @staticmethod + def importer_assembly_standard_json_file(json_file: str, deployed_contract: Optional[str] = None, + final_file: Optional[str] = None, solc_executable: str = "solc"): + compilation = SolidityCompilation(final_file, solc_executable) + compilation.flags = "--standard-json" + compilation.process_output_function = process_importer_standard_output + return compilation.compile_single_sol_file(json_file, deployed_contract, "bin") + @staticmethod def from_multi_sol(multi_sol_info: Dict[str, Any], deployed_contract: str, final_file: Optional[str] = None, solc_executable: str = "solc") -> Optional[Dict[str, Yul_CFG_T]]: diff --git a/src/solution_generation/reconstruct_bytecode.py b/src/solution_generation/reconstruct_bytecode.py index 03159c94..c0c76138 100644 --- a/src/solution_generation/reconstruct_bytecode.py +++ b/src/solution_generation/reconstruct_bytecode.py @@ -380,8 +380,55 @@ def store_asm_output(json_object: Dict[str, Any], object_name: str, cfg_dir: Pat json.dump(json_object, f, indent=4) return file_to_store +def store_asm_standard_json_output(json_object: Dict[str, Any], object_name: str, cfg_dir: Path) -> Path: + file_to_store = cfg_dir.joinpath(object_name + "_standard_json_output.json") + output_file = build_standard_json_output(json_object, object_name) + with open(file_to_store, 'w') as f: + json.dump(output_file, f, indent=4) + return file_to_store def store_binary_output(object_name: str, evm_code: str, cfg_dir: Path) -> None: file_to_store = cfg_dir.joinpath(object_name + "_bin.evm") with open(file_to_store, 'w') as f: f.write(evm_code) + + +def build_standard_json_output(json_object: Dict[str, Any], object_name : str) -> Dict[str,Any]: + output = {} + + output["language"] = "EVMAssembly" + build_standard_json_settings(output) + + output["sources"] = {} + output["sources"][object_name] = {} + output["sources"][object_name]["assemblyJson"] = json_object + + + return output + +def build_standard_json_settings(output_json): + output_json["settings"] = {} + + output = build_output_selection() + output_json["settings"]["outputSelection"] = output + + opt_config = build_optimizer_configuration() + output_json["settings"]["optimizer"] = opt_config + + output_json["settings"]["viaIR"] = True + output_json["settings"]["metadata"] = {} + output_json["settings"]["metadata"]["appendCBOR"] = False + +def build_output_selection(): + output_selection = {} + output_selection["*"] = {} + output_selection["*"][""] = ["*"] + + return output_selection + +def build_optimizer_configuration(): + config = {} + config["enabled"] = True + config["runs"] = 200 + + return config From 1e35e4c7a253431e6aedbef9eb15e6f453bdf8aa Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:52:28 +0100 Subject: [PATCH 2/3] Enable via-ir by default in sol compilation --- src/execution/sol_compilation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/execution/sol_compilation.py b/src/execution/sol_compilation.py index 5a4234d8..d7d3d003 100644 --- a/src/execution/sol_compilation.py +++ b/src/execution/sol_compilation.py @@ -239,7 +239,7 @@ def __init__(self, final_file: Optional[str], solc_command: str): self._solc_command: str = solc_command # TODO: decide how to represent via-ir - self._via_ir = False + self._via_ir = True self._yul_setting = False # Function to select the information from the contract From 9927244a21e03b992ba45c307973ee9af42904d9 Mon Sep 17 00:00:00 2001 From: alexcere Date: Wed, 26 Feb 2025 13:00:30 +0100 Subject: [PATCH 3/3] Add new script to remove gas inference as part of testrunner --- scripts/compare_outputs.py | 28 ++++++++++++++++++++++++++++ scripts/experiments_python.py | 24 ++++++++++++++++-------- 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 scripts/compare_outputs.py diff --git a/scripts/compare_outputs.py b/scripts/compare_outputs.py new file mode 100644 index 00000000..a8063934 --- /dev/null +++ b/scripts/compare_outputs.py @@ -0,0 +1,28 @@ +import json +from jsondiff import diff + +def compare_files(json_file1, json_file2): + with open(json_file1, 'r') as f: + json1 = json.load(f) + + with open(json_file2, 'r') as f: + json2 = json.load(f) + + if json1.keys() != json2.keys(): + print("JSONS have different contract fields") + return False + + # Drop keys for gas + for key, json1_answers in json1.items(): + json2_answers = json2[key] + + for answer in [*json1_answers, *json2_answers]: + answer.pop("gasUsed") + answer.pop("gasUsedForDeposit") + + answer = diff(json1, json2) + print("FINAL", answer) + return answer + +if __name__ == "__main__": + pass diff --git a/scripts/experiments_python.py b/scripts/experiments_python.py index 9520eb5e..38b9d582 100644 --- a/scripts/experiments_python.py +++ b/scripts/experiments_python.py @@ -7,6 +7,7 @@ import sys import pandas as pd from count_num_ins import instrs_from_opcodes +from compare_outputs import compare_files def combine_dfs(csv_folder: Path, combined_csv: Path): @@ -72,25 +73,31 @@ def execute_yul_test(yul_file: str, csv_folder: Path) -> None: print(" ".join(replace_command)) testrunner_command_original = [ - "/experiments/Systems/solidity/build/test/tools/testrunner", - "/experiments/Systems/evmone/build/lib/libevmone.so.0.13.0", + "/system/experiments/Systems/solidity/build/test/tools/testrunner", + "/system/experiments/Systems/evmone_ahernandez/build/lib/libevmone.so", test_file, os.path.join(yul_dir, "resultOriginal.json"), ] subprocess.run(testrunner_command_original) testrunner_command_grey = [ - "/experiments/Systems/solidity/build/test/tools/testrunner", - "/experiments/Systems/evmone/build/lib/libevmone.so.0.13.0", - f"{yul_dir}/test", + "/system/experiments/Systems/solidity/build/test/tools/testrunner", + "/system/experiments/Systems/evmone_ahernandez/build/lib/libevmone.so", + f"{yul_dir}/test_grey", os.path.join(yul_dir, "resultGrey.json"), ] + # print(' '.join(testrunner_command_grey)) subprocess.run(testrunner_command_grey) # Compare results result_original = os.path.join(yul_dir, "resultOriginal.json") result_grey = os.path.join(yul_dir, "resultGrey.json") - if filecmp.cmp(result_original, result_grey, shallow=False): + + # We need to compare them dropping the gas usage + file_comparison = compare_files(result_original, result_grey) + + # If file_comparison is empty, it means that the match is precise + if len(file_comparison) == 0: print("[RES]: Test passed.") result_dict = instrs_from_opcodes(output_file, log_file) csv_file = csv_folder.joinpath("correctos").joinpath(csv_name) @@ -109,8 +116,9 @@ def run_experiments(n_cpus): # Change the directory to the root os.chdir("..") - DIRECTORIO_TESTS = "examples/test/semanticTests" + # DIRECTORIO_TESTS = "examples/test/semanticTests" # DIRECTORIO_TESTS = "tests_evmone" + DIRECTORIO_TESTS = "falla_test/" CSV_FOLDER = Path("csvs") CSV_FOLDER.mkdir(exist_ok=True, parents=True) CSV_FOLDER.joinpath("correctos").mkdir(exist_ok=True, parents=True) @@ -127,7 +135,7 @@ def run_experiments(n_cpus): with mp.Pool(n_cpus) as p: p.starmap(execute_yul_test, [[file, CSV_FOLDER] for file in yul_files]) print("Procesamiento completado.") - combine_dfs(CSV_FOLDER, Path("combined.csv")) + # combine_dfs(CSV_FOLDER, Path("combined.csv")) if __name__ == "__main__":