From a7852cba75e58b86955a0b8a5015b8282ca406b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 17 Dec 2021 22:42:45 +0100 Subject: [PATCH] Python script for parsing eth-gas-reporter output --- scripts/externalTests/parse_eth_gas_report.py | 269 +++++++++++++ .../fixtures/eth_gas_report_gnosis.rst | 381 ++++++++++++++++++ ...test_externalTests_parse_eth_gas_report.py | 219 ++++++++++ 3 files changed, 869 insertions(+) create mode 100755 scripts/externalTests/parse_eth_gas_report.py create mode 100644 test/scripts/fixtures/eth_gas_report_gnosis.rst create mode 100644 test/scripts/test_externalTests_parse_eth_gas_report.py diff --git a/scripts/externalTests/parse_eth_gas_report.py b/scripts/externalTests/parse_eth_gas_report.py new file mode 100755 index 000000000..c5cc58e58 --- /dev/null +++ b/scripts/externalTests/parse_eth_gas_report.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +# coding=utf-8 + +from dataclasses import asdict, dataclass, field +from typing import Dict, Optional, Tuple +import json +import re +import sys + +REPORT_HEADER_REGEX = re.compile(r''' + ^[|\s]+ Solc[ ]version:\s*(?P[\w\d.]+) + [|\s]+ Optimizer[ ]enabled:\s*(?P[\w]+) + [|\s]+ Runs:\s*(?P[\d]+) + [|\s]+ Block[ ]limit:\s*(?P[\d]+)\s*gas + [|\s]+$ +''', re.VERBOSE) +METHOD_HEADER_REGEX = re.compile(r'^[|\s]+Methods[|\s]+$') +METHOD_COLUMN_HEADERS_REGEX = re.compile(r''' + ^[|\s]+ Contract + [|\s]+ Method + [|\s]+ Min + [|\s]+ Max + [|\s]+ Avg + [|\s]+ \#[ ]calls + [|\s]+ \w+[ ]\(avg\) + [|\s]+$ +''', re.VERBOSE) +METHOD_ROW_REGEX = re.compile(r''' + ^[|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+$ +''', re.VERBOSE) +FRAME_REGEX = re.compile(r'^[-|\s]+$') +DEPLOYMENT_HEADER_REGEX = re.compile(r'^[|\s]+Deployments[|\s]+% of limit[|\s]+$') +DEPLOYMENT_ROW_REGEX = re.compile(r''' + ^[|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+) + [|\s]+ (?P[^|]+)\s*% + [|\s]+ (?P[^|]+) + [|\s]+$ +''', re.VERBOSE) + + +class ReportError(Exception): + pass + +class ReportValidationError(ReportError): + pass + +class ReportParsingError(Exception): + def __init__(self, message: str, line: str, line_number: int): + # pylint: disable=useless-super-delegation # It's not useless, it adds type annotations. + super().__init__(message, line, line_number) + + def __str__(self): + return f"Parsing error on line {self.args[2] + 1}: {self.args[0]}\n{self.args[1]}" + + +@dataclass(frozen=True) +class MethodGasReport: + min_gas: int + max_gas: int + avg_gas: int + call_count: int + total_gas: int = field(init=False) + + def __post_init__(self): + object.__setattr__(self, 'total_gas', self.avg_gas * self.call_count) + + +@dataclass(frozen=True) +class ContractGasReport: + min_deployment_gas: Optional[int] + max_deployment_gas: Optional[int] + avg_deployment_gas: Optional[int] + methods: Optional[Dict[str, MethodGasReport]] + total_method_gas: int = field(init=False, default=0) + + def __post_init__(self): + if self.methods is not None: + object.__setattr__(self, 'total_method_gas', sum(method.total_gas for method in self.methods.values())) + + +@dataclass(frozen=True) +class GasReport: + solc_version: str + optimize: bool + runs: int + block_limit: int + contracts: Dict[str, ContractGasReport] + total_method_gas: int = field(init=False) + total_deployment_gas: int = field(init=False) + + def __post_init__(self): + object.__setattr__(self, 'total_method_gas', sum( + total_method_gas + for total_method_gas in (contract.total_method_gas for contract in self.contracts.values()) + if total_method_gas is not None + )) + object.__setattr__(self, 'total_deployment_gas', sum( + contract.avg_deployment_gas + for contract in self.contracts.values() + if contract.avg_deployment_gas is not None + )) + + def to_json(self): + return json.dumps(asdict(self), indent=4, sort_keys=True) + + +def parse_bool(input_string: str) -> bool: + if input_string == 'true': + return True + elif input_string == 'false': + return True + else: + raise ValueError(f"Invalid boolean value: '{input_string}'") + + +def parse_optional_int(input_string: str, default: Optional[int] = None) -> Optional[int]: + if input_string.strip() == '-': + return default + + return int(input_string) + + +def parse_report_header(line: str) -> Optional[dict]: + match = REPORT_HEADER_REGEX.match(line) + if match is None: + return None + + return { + 'solc_version': match.group('solc_version'), + 'optimize': parse_bool(match.group('optimize')), + 'runs': int(match.group('runs')), + 'block_limit': int(match.group('block_limit')), + } + + +def parse_method_row(line: str, line_number: int) -> Optional[Tuple[str, str, MethodGasReport]]: + match = METHOD_ROW_REGEX.match(line) + if match is None: + raise ReportParsingError("Expected a table row with method details.", line, line_number) + + avg_gas = parse_optional_int(match['avg']) + call_count = int(match['call_count']) + + if avg_gas is None and call_count == 0: + # No calls, no gas values. Uninteresting. Skip the row. + return None + + return ( + match['contract'].strip(), + match['method'].strip(), + MethodGasReport( + min_gas=parse_optional_int(match['min'], avg_gas), + max_gas=parse_optional_int(match['max'], avg_gas), + avg_gas=avg_gas, + call_count=call_count, + ) + ) + + +def parse_deployment_row(line: str, line_number: int) -> Tuple[str, int, int, int]: + match = DEPLOYMENT_ROW_REGEX.match(line) + if match is None: + raise ReportParsingError("Expected a table row with deployment details.", line, line_number) + + return ( + match['contract'].strip(), + parse_optional_int(match['min'].strip()), + parse_optional_int(match['max'].strip()), + int(match['avg'].strip()), + ) + + +def preprocess_unicode_frames(input_string: str) -> str: + # The report has a mix of normal pipe chars and its unicode variant. + # Let's just replace all frame chars with normal pipes for easier parsing. + return input_string.replace('\u2502', '|').replace('·', '|') + + +def parse_report(rst_report: str) -> GasReport: + report_params = None + methods_by_contract = {} + deployment_costs = {} + expected_row_type = None + + for line_number, line in enumerate(preprocess_unicode_frames(rst_report).splitlines()): + try: + if ( + line.strip() == "" or + FRAME_REGEX.match(line) is not None or + METHOD_COLUMN_HEADERS_REGEX.match(line) is not None + ): + continue + if METHOD_HEADER_REGEX.match(line) is not None: + expected_row_type = 'method' + continue + if DEPLOYMENT_HEADER_REGEX.match(line) is not None: + expected_row_type = 'deployment' + continue + + new_report_params = parse_report_header(line) + if new_report_params is not None: + if report_params is not None: + raise ReportParsingError("Duplicate report header.", line, line_number) + + report_params = new_report_params + continue + + if expected_row_type == 'method': + parsed_row = parse_method_row(line, line_number) + if parsed_row is None: + continue + + (contract, method, method_report) = parsed_row + + if contract not in methods_by_contract: + methods_by_contract[contract] = {} + + if method in methods_by_contract[contract]: + # Report must be generated with full signatures for method names to be unambiguous. + raise ReportParsingError(f"Duplicate method row for '{contract}.{method}'.", line, line_number) + + methods_by_contract[contract][method] = method_report + elif expected_row_type == 'deployment': + (contract, min_gas, max_gas, avg_gas) = parse_deployment_row(line, line_number) + + if contract in deployment_costs: + raise ReportParsingError(f"Duplicate contract deployment row for '{contract}'.", line, line_number) + + deployment_costs[contract] = (min_gas, max_gas, avg_gas) + else: + assert expected_row_type is None + raise ReportParsingError("Found data row without a section header.", line, line_number) + + except ValueError as error: + raise ReportParsingError(error.args[0], line, line_number) from error + + if report_params is None: + raise ReportValidationError("Report header not found.") + + report_params['contracts'] = { + contract: ContractGasReport( + min_deployment_gas=deployment_costs.get(contract, (None, None, None))[0], + max_deployment_gas=deployment_costs.get(contract, (None, None, None))[1], + avg_deployment_gas=deployment_costs.get(contract, (None, None, None))[2], + methods=methods_by_contract.get(contract), + ) + for contract in methods_by_contract.keys() | deployment_costs.keys() + } + + return GasReport(**report_params) + + +if __name__ == "__main__": + try: + report = parse_report(sys.stdin.read()) + print(report.to_json()) + except ReportError as exception: + print(f"{exception}", file=sys.stderr) + sys.exit(1) diff --git a/test/scripts/fixtures/eth_gas_report_gnosis.rst b/test/scripts/fixtures/eth_gas_report_gnosis.rst new file mode 100644 index 000000000..c2e77f9be --- /dev/null +++ b/test/scripts/fixtures/eth_gas_report_gnosis.rst @@ -0,0 +1,381 @@ +·----------------------------------------------------------------------------------------------------------------------------------------|---------------------------|-------------|------------------------------· +| Solc version: 0.8.10 · Optimizer enabled: true · Runs: 200 · Block limit: 100000000 gas │ +·········································································································································|···························|·············|······························· +| Methods │ +·································|·······································································································|·············|·············|·············|···············|··············· +| Contract · Method · Min · Max · Avg · # calls · eur (avg) │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · getMessageHash(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · getMessageHashForSafe(address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · getModules() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · isValidSignature(bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · isValidSignature(bytes32,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · NAME() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · onERC1155BatchReceived(address,address,uint256[],uint256[],bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · onERC1155Received(address,address,uint256,uint256,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · onERC721Received(address,address,uint256,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · simulate(address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · supportsInterface(bytes4) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · tokensReceived(address,address,address,uint256,bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CompatibilityFallbackHandler · VERSION() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CreateCall · performCreate(uint256,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| CreateCall · performCreate2(uint256,bytes,bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DebugTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DebugTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DebugTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DebugTransactionGuard · txNonces(bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · NAME() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · onERC1155BatchReceived(address,address,uint256[],uint256[],bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · onERC1155Received(address,address,uint256,uint256,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · onERC721Received(address,address,uint256,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · supportsInterface(bytes4) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · tokensReceived(address,address,address,uint256,bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DefaultCallbackHandler · VERSION() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DelegateCallTransactionGuard · allowedTarget() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DelegateCallTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DelegateCallTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| DelegateCallTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC1155Token · balanceOf(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC1155Token · mint(address,uint256,uint256,bytes) · 47934 · 59804 · 57826 · 6 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC1155Token · safeTransferFrom(address,address,uint256,uint256,bytes) · - · - · 53900 · 2 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · allowance(address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · approve(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · balanceOf(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · decimals() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · decreaseAllowance(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · increaseAllowance(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · name() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · symbol() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · totalSupply() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · transfer(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20 · transferFrom(address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · allowance(address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · approve(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · balanceOf(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · decimals() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · decreaseAllowance(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · increaseAllowance(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · name() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · symbol() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · totalSupply() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · transfer(address,uint256) · - · - · 51567 · 8 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ERC20Token · transferFrom(address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| FallbackManager · setFallbackHandler(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · approvedHashes(address,bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · approveHash(bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · changeThreshold(uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · checkNSignatures(bytes32,bytes,bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · checkSignatures(bytes32,bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · disableModule(address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · domainSeparator() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · enableModule(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · encodeTransactionData(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes) · 59563 · 151736 · 94816 · 85 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getChainId() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getModulesPaginated(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getOwners() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getStorageAt(uint256,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getThreshold() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · isModuleEnabled(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · isOwner(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · nonce() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · removeOwner(address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · requiredTxGas(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · setFallbackHandler(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · setGuard(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · setup(address[],uint256,address,bytes,address,address,uint256,address) · 167642 · 263690 · 201944 · 49 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · signedMessages(bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · simulateAndRevert(address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · swapOwner(address,address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafe · VERSION() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · approvedHashes(address,bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · approveHash(bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · changeThreshold(uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · checkNSignatures(bytes32,bytes,bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · checkSignatures(bytes32,bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · disableModule(address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · domainSeparator() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · enableModule(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · encodeTransactionData(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getChainId() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getModulesPaginated(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getOwners() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getStorageAt(uint256,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getThreshold() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · isModuleEnabled(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · isOwner(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · nonce() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · removeOwner(address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · requiredTxGas(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · setFallbackHandler(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · setGuard(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · setup(address[],uint256,address,bytes,address,address,uint256,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · signedMessages(bytes32) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · simulateAndRevert(address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · swapOwner(address,address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeL2 · VERSION() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · calculateCreateProxyWithNonceAddress(address,bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · createProxy(address,bytes) · 105568 · 105580 · 105568 · 52 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · createProxyWithCallback(address,bytes,uint256,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · createProxyWithNonce(address,bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · proxyCreationCode() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GnosisSafeProxyFactory · proxyRuntimeCode() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| GuardManager · setGuard(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| Migration · migrate() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| Migration · migrationSingleton() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| Migration · safe120Singleton() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · DEFAULT_FALLBACK_VALUE() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyReturn(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyReturnAddress(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyReturnBool(bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyReturnUint(uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyRevert() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyRevertWithMessage(string) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenAnyRunOutOfGas() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataReturn(bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataReturnAddress(bytes,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataReturnBool(bytes,bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataReturnUint(bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataRevert(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataRevertWithMessage(bytes,string) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenCalldataRunOutOfGas(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodReturn(bytes,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodReturnAddress(bytes,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodReturnBool(bytes,bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodReturnUint(bytes,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodRevert(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodRevertWithMessage(bytes,string) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · givenMethodRunOutOfGas(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · invocationCount() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · invocationCountForCalldata(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · invocationCountForMethod(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · MOCKS_LIST_END_HASH() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · MOCKS_LIST_END() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · MOCKS_LIST_START() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · reset() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · SENTINEL_ANY_MOCKS() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MockContract · updateInvocationCount(bytes4,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · disableModule(address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · enableModule(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · getModulesPaginated(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ModuleManager · isModuleEnabled(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MultiSend · multiSend(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| MultiSendCallOnly · multiSend(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · changeThreshold(uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · getOwners() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · getThreshold() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · isOwner(address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · removeOwner(address,address,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| OwnerManager · swapOwner(address,address,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ReentrancyTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ReentrancyTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| ReentrancyTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| SignMessageLib · getMessageHash(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| SignMessageLib · signMessage(bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| SimulateTxAccessor · simulate(address,uint256,bytes,uint8) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| StorageAccessible · getStorageAt(uint256,uint256) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| StorageAccessible · simulateAndRevert(address,bytes) · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| TestHandler · dudududu() · - · - · - · 0 · - │ +·································|·······································································································|·············|·············|·············|···············|··············· +| Deployments · · % of limit · │ +·········································································································································|·············|·············|·············|···············|··············· +| DelegateCallTransactionGuard · 283510 · 283522 · 283516 · 0.3 % · - │ +·········································································································································|·············|·············|·············|···············|··············· +| ERC1155Token · - · - · 525869 · 0.5 % · - │ +·········································································································································|·············|·············|·············|···············|··············· +| ERC20Token · - · - · 733462 · 0.7 % · - │ +·----------------------------------------------------------------------------------------------------------------------------------------|-------------|-------------|-------------|---------------|--------------· diff --git a/test/scripts/test_externalTests_parse_eth_gas_report.py b/test/scripts/test_externalTests_parse_eth_gas_report.py new file mode 100644 index 000000000..cefa0487a --- /dev/null +++ b/test/scripts/test_externalTests_parse_eth_gas_report.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +from dataclasses import asdict +import unittest + +from textwrap import dedent + +from unittest_helpers import FIXTURE_DIR, load_fixture + +# 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 externalTests.parse_eth_gas_report import parse_report, ReportParsingError, ReportValidationError +# pragma pylint: enable=import-error + +ETH_GAS_REPORT_GNOSIS_RST_PATH = FIXTURE_DIR / 'eth_gas_report_gnosis.rst' +ETH_GAS_REPORT_GNOSIS_RST_CONTENT = load_fixture(ETH_GAS_REPORT_GNOSIS_RST_PATH) + + +class TestEthGasReport(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + def test_parse_report(self): + parsed_report = parse_report(ETH_GAS_REPORT_GNOSIS_RST_CONTENT) + + expected_report = { + 'solc_version': '0.8.10', + 'optimize': True, + 'runs': 200, + 'block_limit': 100000000, + 'total_method_gas': 57826 * 6 + 53900 * 2 + 51567 * 8 + 94816 * 85 + 201944 * 49 + 105568 * 52, + 'total_deployment_gas': 283516 + 525869 + 733462, + 'contracts': { + 'DelegateCallTransactionGuard': { + 'total_method_gas': 0, + 'min_deployment_gas': 283510, + 'max_deployment_gas': 283522, + 'avg_deployment_gas': 283516, + 'methods': None, + }, + 'ERC1155Token': { + 'total_method_gas': 57826 * 6 + 53900 * 2, + 'min_deployment_gas': None, + 'max_deployment_gas': None, + 'avg_deployment_gas': 525869, + 'methods': { + 'mint(address,uint256,uint256,bytes)': { + 'total_gas': 57826 * 6, + 'min_gas': 47934, + 'max_gas': 59804, + 'avg_gas': 57826, + 'call_count': 6 + }, + 'safeTransferFrom(address,address,uint256,uint256,bytes)': { + 'total_gas': 53900 * 2, + 'min_gas': 53900, + 'max_gas': 53900, + 'avg_gas': 53900, + 'call_count': 2, + }, + }, + }, + 'ERC20Token': { + 'total_method_gas': 51567 * 8, + 'min_deployment_gas': None, + 'max_deployment_gas': None, + 'avg_deployment_gas': 733462, + 'methods': { + 'transfer(address,uint256)': { + 'total_gas': 51567 * 8, + 'min_gas': 51567, + 'max_gas': 51567, + 'avg_gas': 51567, + 'call_count': 8, + }, + }, + }, + 'GnosisSafe': { + 'total_method_gas': 94816 * 85 + 201944 * 49, + 'min_deployment_gas': None, + 'max_deployment_gas': None, + 'avg_deployment_gas': None, + 'methods': { + 'execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)': { + 'total_gas': 94816 * 85, + 'min_gas': 59563, + 'max_gas': 151736, + 'avg_gas': 94816, + 'call_count': 85, + }, + 'setup(address[],uint256,address,bytes,address,address,uint256,address)': { + 'total_gas': 201944 * 49, + 'min_gas': 167642, + 'max_gas': 263690, + 'avg_gas': 201944, + 'call_count': 49, + }, + }, + }, + 'GnosisSafeProxyFactory': { + 'total_method_gas': 105568 * 52, + 'min_deployment_gas': None, + 'max_deployment_gas': None, + 'avg_deployment_gas': None, + 'methods': { + 'createProxy(address,bytes)': { + 'total_gas': 105568 * 52, + 'min_gas': 105568, + 'max_gas': 105580, + 'avg_gas': 105568, + 'call_count': 52, + }, + }, + }, + } + } + self.assertEqual(asdict(parsed_report), expected_report) + + def test_parse_report_should_fail_if_report_is_empty(self): + text_report = "" + with self.assertRaises(ReportValidationError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), "Report header not found.") + + def test_parse_report_should_fail_if_report_has_no_header(self): + text_report = dedent(""" + | Methods | + | ERC1155Token · mint() · 1 · 3 · 2 · 6 · - | + | Deployments · · % of limit · │ + | ERC1155Token · - · - · 5 · 1 % · - | + """).strip('\n') + with self.assertRaises(ReportValidationError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), "Report header not found.") + + def test_parse_report_should_fail_if_data_rows_have_no_headers(self): + text_report = dedent(""" + | ERC1155Token · mint() · 1 · 3 · 2 · 6 · - | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 1: Found data row without a section header. + | ERC1155Token | mint() | 1 | 3 | 2 | 6 | - | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message) + + def test_parse_report_should_fail_if_report_has_more_than_one_header(self): + text_report = dedent(""" + | Solc version: 0.8.10 · Optimizer enabled: true · Runs: 200 · Block limit: 100000000 gas | + | Solc version: 0.8.9 · Optimizer enabled: false · Runs: 111 · Block limit: 999999999 gas | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 2: Duplicate report header. + | Solc version: 0.8.9 | Optimizer enabled: false | Runs: 111 | Block limit: 999999999 gas | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message) + + def test_parse_report_should_fail_if_row_matching_same_method_call_appears_twice(self): + text_report = dedent(""" + | Methods | + | ERC1155Token · mint() · 47934 · 59804 · 57826 · 6 · - | + | ERC1155Token · mint() · 11111 · 22222 · 33333 · 4 · - | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 3: Duplicate method row for 'ERC1155Token.mint()'. + | ERC1155Token | mint() | 11111 | 22222 | 33333 | 4 | - | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message) + + def test_parse_report_should_fail_if_row_matching_same_contract_deployment_appears_twice(self): + text_report = dedent(""" + | Deployments · · % of limit · │ + | ERC1155Token · - · - · 525869 · 0.5 % · - | + | ERC1155Token · - · - · 111111 · 0.6 % · - | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 3: Duplicate contract deployment row for 'ERC1155Token'. + | ERC1155Token | - | - | 111111 | 0.6 % | - | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message) + + def test_parse_report_should_fail_if_method_row_appears_under_deployments_header(self): + text_report = dedent(""" + | Deployments · · % of limit · │ + | ERC1155Token · mint() · 47934 · 59804 · 57826 · 6 · - | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 2: Expected a table row with deployment details. + | ERC1155Token | mint() | 47934 | 59804 | 57826 | 6 | - | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message) + + def test_parse_report_should_fail_if_deployment_row_appears_under_methods_header(self): + text_report = dedent(""" + | Methods | + | ERC1155Token · - · - · 525869 · 5 · - | + """).strip('\n') + expected_message = dedent(""" + Parsing error on line 2: Expected a table row with method details. + | ERC1155Token | - | - | 525869 | 5 | - | + """).strip('\n') + + with self.assertRaises(ReportParsingError) as manager: + parse_report(text_report) + self.assertEqual(str(manager.exception), expected_message)