prepare_report.py: Add support for switching between CLI and Standard JSON compiler interfaces

This commit is contained in:
Kamil Śliwak 2020-12-22 03:03:17 +01:00
parent 88d2a923ae
commit d71de88bc1
4 changed files with 237 additions and 39 deletions

View File

@ -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<file_name>.+):(?P<contract_name>[^:]+) =======$', re.MULTILINE)
BYTECODE_REGEX = re.compile(r'^Binary:\n(?P<bytecode>.*)$', re.MULTILINE)
METADATA_REGEX = re.compile(r'^Metadata:\n(?P<metadata>\{.*\})$', 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),
)

View File

@ -0,0 +1,24 @@
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" 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}

View File

@ -0,0 +1,11 @@
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" 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

View File

@ -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)