diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 819241e2c..1082d77fe 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -3,13 +3,26 @@ import sys import subprocess import json +import re from argparse import ArgumentParser from dataclasses import dataclass +from enum import Enum from glob import glob from pathlib import Path +from tempfile import TemporaryDirectory from typing import List, Optional, Tuple, Union +CONTRACT_SEPARATOR_PATTERN = re.compile(r'^======= (?P.+):(?P[^:]+) =======$', re.MULTILINE) +BYTECODE_REGEX = re.compile(r'^Binary:\n(?P.*)$', re.MULTILINE) +METADATA_REGEX = re.compile(r'^Metadata:\n(?P\{.*\})$', re.MULTILINE) + + +class CompilerInterface(Enum): + CLI = 'cli' + STANDARD_JSON = 'standard-json' + + @dataclass(frozen=True) class ContractReport: contract_name: str @@ -72,53 +85,110 @@ def parse_standard_json_output(source_file_name: Path, standard_json_output: str return file_report -def prepare_compiler_input(compiler_path: Path, source_file_name: Path, optimize: bool) -> Tuple[List[str], str]: - json_input: dict = { - 'language': 'Solidity', - 'sources': { - str(source_file_name): {'content': load_source(source_file_name)} - }, - 'settings': { - 'optimizer': {'enabled': optimize}, - 'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}}, - 'modelChecker': {'engine': 'none'}, - } - } +def parse_cli_output(source_file_name: Path, cli_output: str) -> FileReport: + # re.split() returns a list containing the text beween pattern occurrences but also inserts the + # content of matched groups in between. It also never omits the empty elements so the number of + # list items is predictable (3 per match + the text before the first match) + output_segments = re.split(CONTRACT_SEPARATOR_PATTERN, cli_output) + assert len(output_segments) % 3 == 1 - command_line = [str(compiler_path), '--standard-json'] - compiler_input = json.dumps(json_input) + if len(output_segments) == 1: + return FileReport(file_name=source_file_name, contract_reports=None) + + file_report = FileReport(file_name=source_file_name, contract_reports=[]) + for file_name, contract_name, contract_output in zip(output_segments[1::3], output_segments[2::3], output_segments[3::3]): + bytecode_match = re.search(BYTECODE_REGEX, contract_output) + metadata_match = re.search(METADATA_REGEX, contract_output) + + assert file_report.contract_reports is not None + file_report.contract_reports.append(ContractReport( + contract_name=contract_name, + file_name=Path(file_name), + bytecode=bytecode_match['bytecode'] if bytecode_match is not None else None, + metadata=metadata_match['metadata'] if metadata_match is not None else None, + )) + + return file_report + + +def prepare_compiler_input(compiler_path: Path, source_file_name: Path, optimize: bool, interface: CompilerInterface) -> Tuple[List[str], str]: + if interface == CompilerInterface.STANDARD_JSON: + json_input: dict = { + 'language': 'Solidity', + 'sources': { + str(source_file_name): {'content': load_source(source_file_name)} + }, + 'settings': { + 'optimizer': {'enabled': optimize}, + 'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}}, + 'modelChecker': {'engine': 'none'}, + } + } + + command_line = [str(compiler_path), '--standard-json'] + compiler_input = json.dumps(json_input) + else: + assert interface == CompilerInterface.CLI + + compiler_options = [str(source_file_name), '--bin', '--metadata', '--model-checker-engine', 'none'] + if optimize: + compiler_options.append('--optimize') + + command_line = [str(compiler_path)] + compiler_options + compiler_input = load_source(source_file_name) return (command_line, compiler_input) -def run_compiler(compiler_path: Path, source_file_name: Path, optimize: bool) -> FileReport: - (command_line, compiler_input) = prepare_compiler_input(compiler_path, Path(source_file_name).name, optimize) +def run_compiler(compiler_path: Path, source_file_name: Path, optimize: bool, interface: CompilerInterface) -> FileReport: + if interface == CompilerInterface.STANDARD_JSON: + (command_line, compiler_input) = prepare_compiler_input(compiler_path, Path(source_file_name).name, optimize, interface) - process = subprocess.run( - command_line, - input=compiler_input, - encoding='utf8', - capture_output=True, - ) + process = subprocess.run( + command_line, + input=compiler_input, + encoding='utf8', + capture_output=True, + ) - return parse_standard_json_output(Path(source_file_name), process.stdout) + return parse_standard_json_output(Path(source_file_name), process.stdout) + else: + assert interface == CompilerInterface.CLI + assert tmp_dir is not None + + (command_line, compiler_input) = prepare_compiler_input(compiler_path.absolute(), source_file_name.name, optimize, interface) + + # Create a copy that we can use directly with the CLI interface + modified_source_path = tmp_dir / source_file_name.name + with open(modified_source_path, 'w') as modified_source_file: + modified_source_file.write(compiler_input) + + process = subprocess.run( + command_line, + cwd=tmp_dir, + encoding='utf8', + capture_output=True, + ) + + return parse_cli_output(Path(source_file_name), process.stdout) -def generate_report(source_file_names: List[str], compiler_path: Path): +def generate_report(source_file_names: List[str], compiler_path: Path, interface: CompilerInterface): with open('report.txt', mode='w', encoding='utf8', newline='\n') as report_file: for optimize in [False, True]: - for source_file_name in sorted(source_file_names): - try: - report = run_compiler(Path(compiler_path), Path(source_file_name), optimize) - report_file.write(report.format_report()) - except subprocess.CalledProcessError as exception: - print(f"\n\nInterrupted by an exception while processing file '{source_file_name}' with optimize={optimize}\n", file=sys.stderr) - print(f"COMPILER STDOUT:\n{exception.stdout}", file=sys.stderr) - print(f"COMPILER STDERR:\n{exception.stderr}", file=sys.stderr) - raise - except: - print(f"\n\nInterrupted by an exception while processing file '{source_file_name}' with optimize={optimize}\n", file=sys.stderr) - raise + with TemporaryDirectory(prefix='prepare_report-') as tmp_dir: + for source_file_name in sorted(source_file_names): + try: + report = run_compiler(Path(compiler_path), Path(source_file_name), optimize, interface, Path(tmp_dir)) + report_file.write(report.format_report()) + except subprocess.CalledProcessError as exception: + print(f"\n\nInterrupted by an exception while processing file '{source_file_name}' with optimize={optimize}\n", file=sys.stderr) + print(f"COMPILER STDOUT:\n{exception.stdout}", file=sys.stderr) + print(f"COMPILER STDERR:\n{exception.stderr}", file=sys.stderr) + raise + except: + print(f"\n\nInterrupted by an exception while processing file '{source_file_name}' with optimize={optimize}\n", file=sys.stderr) + raise def commandline_parser() -> ArgumentParser: @@ -129,6 +199,7 @@ def commandline_parser() -> ArgumentParser: parser = ArgumentParser(description=script_description) parser.add_argument(dest='compiler_path', help="Solidity compiler executable") + parser.add_argument('--interface', dest='interface', default=CompilerInterface.STANDARD_JSON.value, choices=[c.value for c in CompilerInterface], help="Compiler interface to use.") return parser; @@ -137,4 +208,5 @@ if __name__ == "__main__": generate_report( glob("*.sol"), Path(options.compiler_path), + CompilerInterface(options.interface), ) diff --git a/test/scripts/fixtures/library_inherited2_sol_cli_output.txt b/test/scripts/fixtures/library_inherited2_sol_cli_output.txt new file mode 100644 index 000000000..45aa83f78 --- /dev/null +++ b/test/scripts/fixtures/library_inherited2_sol_cli_output.txt @@ -0,0 +1,24 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> syntaxTests/scoping/library_inherited2.sol + +Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.8.0;" +--> syntaxTests/scoping/library_inherited2.sol + + +======= syntaxTests/scoping/library_inherited2.sol:A ======= +Binary: +6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122086e727f29d40b264a19bbfcad38d64493dca4bab5dbba8c82ffdaae389d2bba064736f6c63430008000033 +Metadata: +{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"A"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1} + +======= syntaxTests/scoping/library_inherited2.sol:B ======= +Binary: +608060405234801561001057600080fd5b506101cc806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630423a13214610030575b600080fd5b61004a6004803603810190610045919061009d565b610060565b60405161005791906100d5565b60405180910390f35b600061006b82610072565b9050919050565b6000602a8261008191906100f0565b9050919050565b6000813590506100978161017f565b92915050565b6000602082840312156100af57600080fd5b60006100bd84828501610088565b91505092915050565b6100cf81610146565b82525050565b60006020820190506100ea60008301846100c6565b92915050565b60006100fb82610146565b915061010683610146565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561013b5761013a610150565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61018881610146565b811461019357600080fd5b5056fea2646970667358221220104c345633313efe410492448844d96d78452c3044ce126b5e041b7fbeaa790064736f6c63430008000033 +Metadata: +{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"bar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"B"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1} + +======= syntaxTests/scoping/library_inherited2.sol:Lib ======= +Binary: +60566050600b82828239805160001a6073146043577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207f9515e2263fa71a7984707e2aefd82241fac15c497386ca798b526f14f8ba6664736f6c63430008000033 +Metadata: +{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"Lib"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1} diff --git a/test/scripts/fixtures/unknown_pragma_sol_cli_output.txt b/test/scripts/fixtures/unknown_pragma_sol_cli_output.txt new file mode 100644 index 000000000..3a4e8aba1 --- /dev/null +++ b/test/scripts/fixtures/unknown_pragma_sol_cli_output.txt @@ -0,0 +1,11 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> syntaxTests/pragma/unknown_pragma.sol + +Error: Unknown pragma "thisdoesntexist" +--> syntaxTests/pragma/unknown_pragma.sol:1:1: +| +1 | pragma thisdoesntexist; +| ^^^^^^^^^^^^^^^^^^^^^^^ + +Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.8.0;" +--> syntaxTests/pragma/unknown_pragma.sol diff --git a/test/scripts/test_bytecodecompare_prepare_report.py b/test/scripts/test_bytecodecompare_prepare_report.py index ad91ac268..6589d7e74 100644 --- a/test/scripts/test_bytecodecompare_prepare_report.py +++ b/test/scripts/test_bytecodecompare_prepare_report.py @@ -7,8 +7,8 @@ from textwrap import dedent from unittest_helpers import LIBSOLIDITY_TEST_DIR, load_fixture, load_libsolidity_test_case -from bytecodecompare.prepare_report import FileReport, ContractReport -from bytecodecompare.prepare_report import load_source, parse_standard_json_output, prepare_compiler_input +from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport +from bytecodecompare.prepare_report import load_source, parse_cli_output, parse_standard_json_output, prepare_compiler_input SMT_SMOKE_TEST_SOL_PATH = LIBSOLIDITY_TEST_DIR / 'smtCheckerTests/simple/smoke_test.sol' @@ -18,8 +18,10 @@ SYNTAX_SMOKE_TEST_SOL_PATH = LIBSOLIDITY_TEST_DIR / 'syntaxTests/smoke_test.sol' SYNTAX_SMOKE_TEST_SOL_CODE = load_libsolidity_test_case(SYNTAX_SMOKE_TEST_SOL_PATH) LIBRARY_INHERITED2_SOL_JSON_OUTPUT = load_fixture('library_inherited2_sol_json_output.json') +LIBRARY_INHERITED2_SOL_CLI_OUTPUT = load_fixture('library_inherited2_sol_cli_output.txt') UNKNOWN_PRAGMA_SOL_JSON_OUTPUT = load_fixture('unknown_pragma_sol_json_output.json') +UNKNOWN_PRAGMA_SOL_CLI_OUTPUT = load_fixture('unknown_pragma_sol_cli_output.txt') class TestPrepareReport_FileReport(unittest.TestCase): @@ -81,7 +83,7 @@ class TestPrepareReport(unittest.TestCase): def test_load_source(self): self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH), SMT_SMOKE_TEST_SOL_CODE) - def test_prepare_compiler_input(self): + def test_prepare_compiler_input_should_work_with_standard_json_interface(self): expected_compiler_input = { 'language': 'Solidity', 'sources': { @@ -98,11 +100,23 @@ class TestPrepareReport(unittest.TestCase): Path('solc'), SMT_SMOKE_TEST_SOL_PATH, optimize=True, + interface=CompilerInterface.STANDARD_JSON, ) self.assertEqual(command_line, ['solc', '--standard-json']) self.assertEqual(json.loads(compiler_input), expected_compiler_input) + def test_prepare_compiler_input_should_work_with_cli_interface(self): + (command_line, compiler_input) = prepare_compiler_input( + Path('solc'), + SMT_SMOKE_TEST_SOL_PATH, + optimize=True, + interface=CompilerInterface.CLI, + ) + + self.assertEqual(command_line, ['solc', str(SMT_SMOKE_TEST_SOL_PATH), '--bin', '--metadata', '--model-checker-engine', 'none', '--optimize']) + self.assertEqual(compiler_input, SMT_SMOKE_TEST_SOL_CODE) + def test_parse_standard_json_output(self): expected_report = FileReport( file_name=Path('syntaxTests/scoping/library_inherited2.sol'), @@ -177,3 +191,80 @@ class TestPrepareReport(unittest.TestCase): ) self.assertEqual(parse_standard_json_output(Path('contract.sol'), compiler_output), expected_report) + + def test_parse_cli_output(self): + expected_report = FileReport( + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + contract_reports=[ + ContractReport( + contract_name='A', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode='6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122086e727f29d40b264a19bbfcad38d64493dca4bab5dbba8c82ffdaae389d2bba064736f6c63430008000033', + metadata='{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"A"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1}', + ), + ContractReport( + contract_name='B', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode='608060405234801561001057600080fd5b506101cc806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630423a13214610030575b600080fd5b61004a6004803603810190610045919061009d565b610060565b60405161005791906100d5565b60405180910390f35b600061006b82610072565b9050919050565b6000602a8261008191906100f0565b9050919050565b6000813590506100978161017f565b92915050565b6000602082840312156100af57600080fd5b60006100bd84828501610088565b91505092915050565b6100cf81610146565b82525050565b60006020820190506100ea60008301846100c6565b92915050565b60006100fb82610146565b915061010683610146565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561013b5761013a610150565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61018881610146565b811461019357600080fd5b5056fea2646970667358221220104c345633313efe410492448844d96d78452c3044ce126b5e041b7fbeaa790064736f6c63430008000033', + metadata='{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"bar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"B"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1}', + ), + ContractReport( + contract_name='Lib', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode='60566050600b82828239805160001a6073146043577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207f9515e2263fa71a7984707e2aefd82241fac15c497386ca798b526f14f8ba6664736f6c63430008000033', + metadata='{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"Lib"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1}', + ), + ] + ) + + self.assertEqual(parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), LIBRARY_INHERITED2_SOL_CLI_OUTPUT), expected_report) + + def test_parse_cli_output_should_report_error_on_compiler_errors(self): + expected_report = FileReport(file_name=Path('syntaxTests/pragma/unknown_pragma.sol'), contract_reports=None) + + self.assertEqual(parse_cli_output(Path('syntaxTests/pragma/unknown_pragma.sol'), UNKNOWN_PRAGMA_SOL_CLI_OUTPUT), expected_report) + + def test_parse_cli_output_should_report_error_on_empty_output(self): + expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None) + + self.assertEqual(parse_cli_output(Path('file.sol'), ''), expected_report) + + def test_parse_cli_output_should_report_missing_bytecode_and_metadata(self): + compiler_output = dedent("""\ + ======= syntaxTests/scoping/library_inherited2.sol:A ======= + ======= syntaxTests/scoping/library_inherited2.sol:B ======= + 608060405234801561001057600080fd5b506101cc806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630423a13214610030575b600080fd5b61004a6004803603810190610045919061009d565b610060565b60405161005791906100d5565b60405180910390f35b600061006b82610072565b9050919050565b6000602a8261008191906100f0565b9050919050565b6000813590506100978161017f565b92915050565b6000602082840312156100af57600080fd5b60006100bd84828501610088565b91505092915050565b6100cf81610146565b82525050565b60006020820190506100ea60008301846100c6565b92915050565b60006100fb82610146565b915061010683610146565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561013b5761013a610150565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61018881610146565b811461019357600080fd5b5056fea2646970667358221220104c345633313efe410492448844d96d78452c3044ce126b5e041b7fbeaa790064736f6c63430008000033 + Metadata: + {"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"bar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"B"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1} + + ======= syntaxTests/scoping/library_inherited2.sol:Lib ======= + Binary: + 60566050600b82828239805160001a6073146043577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207f9515e2263fa71a7984707e2aefd82241fac15c497386ca798b526f14f8ba6664736f6c63430008000033 + Metadata: + """) + + expected_report = FileReport( + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + contract_reports=[ + ContractReport( + contract_name='A', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode=None, + metadata=None, + ), + ContractReport( + contract_name='B', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode=None, + metadata='{"compiler":{"version":"0.8.0+commit.c7dfd78e"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"bar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"syntaxTests/scoping/library_inherited2.sol":"B"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"syntaxTests/scoping/library_inherited2.sol":{"keccak256":"0xd0619f00638fdfea187368965615dbd599fead93dd14b6558725e85ec7011d96","urls":["bzz-raw://ec7af066be66a223f0d25ba3bf9ba6dc103e1a57531a66a38a5ca2b6ce172f55","dweb:/ipfs/QmW1NrqQNhnY1Tkgr3Z9oM8buCGLUJCJVCDTVejJTT5Vet"]}},"version":1}', + ), + ContractReport( + contract_name='Lib', + file_name=Path('syntaxTests/scoping/library_inherited2.sol'), + bytecode='60566050600b82828239805160001a6073146043577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207f9515e2263fa71a7984707e2aefd82241fac15c497386ca798b526f14f8ba6664736f6c63430008000033', + metadata=None, + ), + ] + ) + + self.assertEqual(parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), compiler_output), expected_report)