Merge pull request #10676 from ethereum/cli-bytecode-comparison

Bytecode comparison via CLI interface
This commit is contained in:
chriseth 2021-01-28 11:58:30 +01:00 committed by GitHub
commit 46bb81d7ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 528 additions and 70 deletions

View File

@ -925,13 +925,19 @@ jobs:
- checkout
- attach_workspace:
at: build
- run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-ubuntu.txt
- run: mkdir test-cases/
- run: cd test-cases && ../scripts/isolate_tests.py ../test/
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface standard-json && mv -v report.txt ../bytecode-report-ubuntu-json.txt
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface cli && mv -v report.txt ../bytecode-report-ubuntu-cli.txt
- store_artifacts:
path: report.txt
path: bytecode-report-ubuntu-json.txt
- store_artifacts:
path: bytecode-report-ubuntu-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-ubuntu.txt
- bytecode-report-ubuntu-json.txt
- bytecode-report-ubuntu-cli.txt
b_bytecode_osx:
macos:
@ -942,13 +948,19 @@ jobs:
- checkout
- attach_workspace:
at: .
- run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-osx.txt
- run: mkdir test-cases/
- run: cd test-cases && ../scripts/isolate_tests.py ../test/
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface standard-json && mv -v report.txt ../bytecode-report-osx-json.txt
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface cli && mv -v report.txt ../bytecode-report-osx-cli.txt
- store_artifacts:
path: report.txt
path: bytecode-report-osx-json.txt
- store_artifacts:
path: bytecode-report-osx-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-osx.txt
- bytecode-report-osx-json.txt
- bytecode-report-osx-cli.txt
b_bytecode_win:
executor:
@ -961,15 +973,19 @@ jobs:
- checkout
- attach_workspace:
at: build
- run: python scripts\isolate_tests.py test\
- run: python scripts\bytecodecompare\prepare_report.py build\solc\Release\solc.exe
- run: cp report.txt bytecode-report-windows.txt
- run: mkdir test-cases\
- run: cd test-cases\ && python ..\scripts\isolate_tests.py ..\test\
- run: cd test-cases\ && python ..\scripts\bytecodecompare\prepare_report.py ..\build\solc\Release\solc.exe --interface standard-json && move report.txt ..\bytecode-report-windows-json.txt
- run: cd test-cases\ && python ..\scripts\bytecodecompare\prepare_report.py ..\build\solc\Release\solc.exe --interface cli && move report.txt ..\bytecode-report-windows-cli.txt
- store_artifacts:
path: report.txt
path: bytecode-report-windows-json.txt
- store_artifacts:
path: bytecode-report-windows-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-windows.txt
- bytecode-report-windows-json.txt
- bytecode-report-windows-cli.txt
b_bytecode_ems:
docker:
@ -980,9 +996,9 @@ jobs:
- checkout
- attach_workspace:
at: emscripten_build/libsolc
- run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-emscripten.txt
- run: scripts/bytecodecompare/storebytecode.sh && mv -v report.txt bytecode-report-emscripten.txt
- store_artifacts:
path: report.txt
path: bytecode-report-emscripten.txt
- persist_to_workspace:
root: .
paths:
@ -994,7 +1010,15 @@ jobs:
steps:
- attach_workspace:
at: .
- run: diff --report-identical-files --from-file bytecode-report-emscripten.txt bytecode-report-ubuntu.txt bytecode-report-osx.txt bytecode-report-windows.txt
- run: |
diff --report-identical-files --from-file \
bytecode-report-emscripten.txt \
bytecode-report-ubuntu-json.txt \
bytecode-report-ubuntu-cli.txt \
bytecode-report-osx-json.txt \
bytecode-report-osx-cli.txt \
bytecode-report-windows-json.txt \
bytecode-report-windows-cli.txt
workflows:
version: 2

View File

@ -24,10 +24,25 @@ for (const optimize of [false, true])
const result = JSON.parse(compiler.compile(JSON.stringify(input)))
let internalCompilerError = false
if ('errors' in result)
{
for (const error of result['errors'])
// JSON interface still returns contract metadata in case of an internal compiler error while
// CLI interface does not. To make reports comparable we must force this case to be detected as
// an error in both cases.
if (['UnimplementedFeatureError', 'CompilerError', 'CodeGenerationError'].includes(error['type']))
{
internalCompilerError = true
break
}
}
if (
!('contracts' in result) ||
Object.keys(result['contracts']).length === 0 ||
Object.keys(result['contracts']).every(file => Object.keys(result['contracts'][file]).length === 0)
Object.keys(result['contracts']).every(file => Object.keys(result['contracts'][file]).length === 0) ||
internalCompilerError
)
// NOTE: do not exit here because this may be run on source which cannot be compiled
console.log(filename + ': <ERROR>')

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
@ -53,10 +66,19 @@ def load_source(path: Union[Path, str]) -> str:
def parse_standard_json_output(source_file_name: Path, standard_json_output: str) -> FileReport:
decoded_json_output = json.loads(standard_json_output.strip())
# JSON interface still returns contract metadata in case of an internal compiler error while
# CLI interface does not. To make reports comparable we must force this case to be detected as
# an error in both cases.
internal_compiler_error = any(
error['type'] in ['UnimplementedFeatureError', 'CompilerError', 'CodeGenerationError']
for error in decoded_json_output.get('errors', {})
)
if (
'contracts' not in decoded_json_output or
len(decoded_json_output['contracts']) == 0 or
all(len(file_results) == 0 for file_name, file_results in decoded_json_output['contracts'].items())
all(len(file_results) == 0 for file_name, file_results in decoded_json_output['contracts'].items()) or
internal_compiler_error
):
return FileReport(file_name=source_file_name, contract_reports=None)
@ -74,7 +96,40 @@ 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]:
def parse_cli_output(source_file_name: Path, cli_output: str) -> FileReport:
# re.split() returns a list containing the text between 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
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': {
@ -89,12 +144,34 @@ def prepare_compiler_input(compiler_path: Path, source_file_name: Path, optimize
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(Path(source_file_name).name), optimize)
def run_compiler(
compiler_path: Path,
source_file_name: Path,
optimize: bool,
interface: CompilerInterface,
tmp_dir: Path,
) -> 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,
@ -105,14 +182,42 @@ def run_compiler(compiler_path: Path, source_file_name: Path, optimize: bool) ->
)
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(),
Path(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
# NOTE: newline='' disables newline conversion.
# We want the file exactly as is because changing even a single byte in the source affects metadata.
with open(modified_source_path, 'w', encoding='utf8', newline='') as modified_source_file:
modified_source_file.write(compiler_input)
process = subprocess.run(
command_line,
cwd=tmp_dir,
encoding='utf8',
capture_output=True,
check=False,
)
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]:
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)
report = run_compiler(compiler_path, Path(source_file_name), optimize, interface, Path(tmp_dir))
report_file.write(report.format_report())
except subprocess.CalledProcessError as exception:
print(
@ -140,6 +245,13 @@ 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;
@ -148,4 +260,5 @@ if __name__ == "__main__":
generate_report(
glob("*.sol"),
Path(options.compiler_path),
CompilerInterface(options.interface),
)

View File

@ -0,0 +1,8 @@
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.
--> test_1c3426238b8296745d8d8bd0ff995ab65a51992b568dc7c5ce73c3f59b107825_no_assignments_sol.sol
Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.8.0;"
--> test_1c3426238b8296745d8d8bd0ff995ab65a51992b568dc7c5ce73c3f59b107825_no_assignments_sol.sol
Error: Some immutables were read from but never assigned, possibly because of optimization.

View File

@ -0,0 +1,50 @@
{
"contracts": {
"syntaxTests/immutable/no_assignments.sol": {
"C": {
"metadata": "{\"compiler\":{\"version\":\"0.8.0+commit.c7dfd78e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"f\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"syntaxTests/immutable/no_assignments.sol\":\"C\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"syntaxTests/immutable/no_assignments.sol\":{\"keccak256\":\"0xbafaec265150d52cd293787144247b1a0a782adf3cb89296fb4f0eb05dc25739\",\"urls\":[\"bzz-raw://7c01dbb8146347c8cf62469d57a0e290f7ef1b871426d86d995315160db665c0\",\"dweb:/ipfs/QmPrYtxVbFCFeXwnhcHoBgbg546EqMzQCT5kK7wLc3rat8\"]}},\"version\":1}"
}
}
},
"errors": [
{
"component": "general",
"errorCode": "1878",
"formattedMessage": "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.\n--> syntaxTests/immutable/no_assignments.sol\n\n",
"message": "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.",
"severity": "warning",
"sourceLocation": {
"end": -1,
"file": "syntaxTests/immutable/no_assignments.sol",
"start": -1
},
"type": "Warning"
},
{
"component": "general",
"errorCode": "3420",
"formattedMessage": "Warning: Source file does not specify required compiler version! Consider adding \"pragma solidity ^0.8.0;\"\n--> syntaxTests/immutable/no_assignments.sol\n\n",
"message": "Source file does not specify required compiler version! Consider adding \"pragma solidity ^0.8.0;\"",
"severity": "warning",
"sourceLocation": {
"end": -1,
"file": "syntaxTests/immutable/no_assignments.sol",
"start": -1
},
"type": "Warning"
},
{
"component": "general",
"errorCode": "1284",
"formattedMessage": "CodeGenerationError: Some immutables were read from but never assigned, possibly because of optimization.\n\n",
"message": "Some immutables were read from but never assigned, possibly because of optimization.",
"severity": "error",
"type": "CodeGenerationError"
}
],
"sources": {
"syntaxTests/immutable/no_assignments.sol": {
"id": 0
}
}
}

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,2 @@
Compiler error: Stack too deep when compiling inline assembly: Variable value0 is 1 slot(s) too deep inside the stack.

View File

@ -0,0 +1,23 @@
{
"contracts": {
"syntaxTests/tupleAssignments/large_component_count.sol": {
"C": {
"metadata": "{\"compiler\":{\"version\":\"0.8.0+commit.c7dfd78e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"f\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"g\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"syntaxTests/tupleAssignments/large_component_count.sol\":\"C\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"syntaxTests/tupleAssignments/large_component_count.sol\":{\"keccak256\":\"0xb5478857c30ab2e7cf6b0fdcad0fdc70f4715129a7dd3f3f1dda5b3892a83846\",\"urls\":[\"bzz-raw://abc14e4a2a61618b712c4ac3ba0cab07d9214d0637af78aa7648e3d2f89eb725\",\"dweb:/ipfs/QmTqrQzwWbZgdAck6ma9xY3sL1mGiw61Nsg36P21mZdxLG\"]}},\"version\":1}"
}
}
},
"errors": [
{
"component": "general",
"formattedMessage": "CompilerError: Stack too deep when compiling inline assembly: Variable value0 is 1 slot(s) too deep inside the stack.\n\n",
"message": "Compiler error (/solidity/libyul/backends/evm/AsmCodeGen.cpp:248):Stack too deep when compiling inline assembly: Variable value0 is 1 slot(s) too deep inside the stack.",
"severity": "error",
"type": "CompilerError"
}
],
"sources": {
"syntaxTests/tupleAssignments/large_component_count.sol": {
"id": 0
}
}
}

View File

@ -0,0 +1,5 @@
Unimplemented feature:
/solidity/libsolidity/codegen/CompilerUtils.cpp(771): Throw in function void solidity::frontend::CompilerUtils::convertType(const solidity::frontend::Type&, const solidity::frontend::Type&, bool, bool, bool)
Dynamic exception type: boost::wrapexcept<solidity::langutil::UnimplementedFeatureError>
std::exception::what: Not yet implemented - FixedPointType.
[solidity::util::tag_comment*] = Not yet implemented - FixedPointType.

View File

@ -0,0 +1,23 @@
{
"contracts": {
"syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol": {
"test": {
"metadata": "{\"compiler\":{\"version\":\"0.8.0+commit.c7dfd78e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol\":\"test\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol\":{\"keccak256\":\"0x44b85b2db00441b574d40d11a6be684517d4312de0f6ef0550e02aea33e2a05f\",\"urls\":[\"bzz-raw://c7a9416a8634c5bd1451742f09d633acab37a6e0db1441d0bb7ea4e8f4af214b\",\"dweb:/ipfs/QmZNMQcZu9wnD6sFH2c7PymZReYeroBXDyKkJ5YEhWtCjc\"]}},\"version\":1}"
}
}
},
"errors": [
{
"component": "general",
"formattedMessage": "UnimplementedFeatureError: Not yet implemented - FixedPointType.\n\n",
"message": "Unimplemented feature (/solidity/libsolidity/codegen/CompilerUtils.cpp:771):Not yet implemented - FixedPointType.",
"severity": "error",
"type": "UnimplementedFeatureError"
}
],
"sources": {
"syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol": {
"id": 0
}
}
}

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

@ -3,13 +3,14 @@
import json
import unittest
from pathlib import Path
from textwrap import dedent
from unittest_helpers import FIXTURE_DIR, LIBSOLIDITY_TEST_DIR, load_fixture, load_libsolidity_test_case
# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports
# pragma pylint: disable=import-error
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
# pragma pylint: enable=import-error
@ -20,16 +21,33 @@ SMT_CONTRACT_WITH_LF_NEWLINES_SOL_PATH = FIXTURE_DIR / 'smt_contract_with_lf_new
SMT_CONTRACT_WITH_CRLF_NEWLINES_SOL_PATH = FIXTURE_DIR / 'smt_contract_with_crlf_newlines.sol'
SMT_CONTRACT_WITH_CR_NEWLINES_SOL_PATH = FIXTURE_DIR / 'smt_contract_with_cr_newlines.sol'
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH = FIXTURE_DIR / 'smt_contract_with_mixed_newlines.sol'
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_CODE = load_fixture(SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH)
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')
UNIMPLEMENTED_FEATURE_JSON_OUTPUT = load_fixture('unimplemented_feature_json_output.json')
UNIMPLEMENTED_FEATURE_CLI_OUTPUT = load_fixture('unimplemented_feature_cli_output.txt')
STACK_TOO_DEEP_JSON_OUTPUT = load_fixture('stack_too_deep_json_output.json')
STACK_TOO_DEEP_CLI_OUTPUT = load_fixture('stack_too_deep_cli_output.txt')
CODE_GENERATION_ERROR_JSON_OUTPUT = load_fixture('code_generation_error_json_output.json')
CODE_GENERATION_ERROR_CLI_OUTPUT = load_fixture('code_generation_error_cli_output.txt')
class TestPrepareReport_FileReport(unittest.TestCase):
class PrepareReportTestBase(unittest.TestCase):
def setUp(self):
self.maxDiff = 10000
class TestFileReport(PrepareReportTestBase):
def test_format_report(self):
report = FileReport(
file_name=Path('syntaxTests/scoping/library_inherited2.sol'),
@ -79,10 +97,7 @@ class TestPrepareReport_FileReport(unittest.TestCase):
self.assertEqual(report.format_report(), '')
class TestPrepareReport(unittest.TestCase):
def setUp(self):
self.maxDiff = 10000
class TestLoadSource(PrepareReportTestBase):
def test_load_source(self):
self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH), SMT_SMOKE_TEST_SOL_CODE)
@ -126,7 +141,9 @@ class TestPrepareReport(unittest.TestCase):
self.assertEqual(load_source(SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH), expected_output)
def test_prepare_compiler_input(self):
class TestPrepareCompilerInput(PrepareReportTestBase):
def test_prepare_compiler_input_should_work_with_standard_json_interface(self):
expected_compiler_input = {
'language': 'Solidity',
'sources': {
@ -143,12 +160,27 @@ 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_preserves_newlines(self):
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_prepare_compiler_input_for_json_preserves_newlines(self):
expected_compiler_input = {
'language': 'Solidity',
'sources': {
@ -171,11 +203,24 @@ class TestPrepareReport(unittest.TestCase):
Path('solc'),
SMT_CONTRACT_WITH_MIXED_NEWLINES_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_for_cli_preserves_newlines(self):
(_command_line, compiler_input) = prepare_compiler_input(
Path('solc'),
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH,
optimize=True,
interface=CompilerInterface.CLI,
)
self.assertEqual(compiler_input, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_CODE)
class TestParseStandardJSONOutput(PrepareReportTestBase):
def test_parse_standard_json_output(self):
expected_report = FileReport(
file_name=Path('syntaxTests/scoping/library_inherited2.sol'),
@ -257,3 +302,118 @@ class TestPrepareReport(unittest.TestCase):
)
self.assertEqual(parse_standard_json_output(Path('contract.sol'), compiler_output), expected_report)
def test_parse_standard_json_output_should_report_error_on_unimplemented_feature_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_standard_json_output(Path('file.sol'), UNIMPLEMENTED_FEATURE_JSON_OUTPUT), expected_report)
def test_parse_standard_json_output_should_report_error_on_stack_too_deep_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_standard_json_output(Path('file.sol'), STACK_TOO_DEEP_JSON_OUTPUT), expected_report)
def test_parse_standard_json_output_should_report_error_on_code_generation_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_standard_json_output(Path('file.sol'), CODE_GENERATION_ERROR_JSON_OUTPUT), expected_report)
class TestParseCLIOutput(PrepareReportTestBase):
def test_parse_cli_output(self):
expected_report = FileReport(
file_name=Path('syntaxTests/scoping/library_inherited2.sol'),
contract_reports=[
# pragma pylint: disable=line-too-long
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}',
),
# pragma pylint: enable=line-too-long
]
)
report = parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), LIBRARY_INHERITED2_SOL_CLI_OUTPUT)
self.assertEqual(report, 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)
report = parse_cli_output(Path('syntaxTests/pragma/unknown_pragma.sol'), UNKNOWN_PRAGMA_SOL_CLI_OUTPUT)
self.assertEqual(report, 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,
),
# pragma pylint: disable=line-too-long
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,
),
# pragma pylint: enable=line-too-long
]
)
self.assertEqual(parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), compiler_output), expected_report)
def test_parse_cli_output_should_report_error_on_unimplemented_feature_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_cli_output(Path('file.sol'), UNIMPLEMENTED_FEATURE_CLI_OUTPUT), expected_report)
def test_parse_cli_output_should_report_error_on_stack_too_deep_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_cli_output(Path('file.sol'), STACK_TOO_DEEP_CLI_OUTPUT), expected_report)
def test_parse_cli_output_should_report_error_on_code_generation_error(self):
expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None)
self.assertEqual(parse_cli_output(Path('file.sol'), CODE_GENERATION_ERROR_CLI_OUTPUT), expected_report)