Merge branch 'develop' into fix/constraints-after-issues

fix/constraints-after-issues
Nathan 5 years ago committed by GitHub
commit 52d5afe0b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      docs/source/index.rst
  2. 63
      docs/source/mythx-analysis.rst
  3. 2
      docs/source/security-analysis.rst
  4. 2
      mythril/__version__.py
  5. 6
      mythril/analysis/templates/report_as_markdown.jinja2
  6. 4
      mythril/analysis/templates/report_as_text.jinja2
  7. 64
      mythril/ethereum/util.py
  8. 103
      mythril/interfaces/cli.py
  9. 7
      mythril/interfaces/old_cli.py
  10. 2
      mythril/laser/ethereum/util.py
  11. 12
      mythril/mythril/mythril_disassembler.py
  12. 111
      mythril/mythx/__init__.py
  13. 77
      mythril/solidity/soliditycontract.py
  14. 47
      mythril/support/signatures.py
  15. 3
      requirements.txt
  16. 3
      setup.py
  17. 6
      tests/disassembler_test.py

@ -9,6 +9,7 @@ Welcome to Mythril's documentation!
installation
security-analysis
analysis-modules
mythx-analysis
mythril

@ -0,0 +1,63 @@
MythX Analysis
=================
Run :code:`myth pro` with one of the input options described below will run a `MythX analysis <https://mythx.io>`_ on the desired input. This includes a run of Mythril, the fuzzer Harvey, and the static analysis engine Maru and has some false-positive filtering only possible by combining the tool capabilities.
**************
Authentication
**************
In order to authenticate with the MythX API, set the environment variables ``MYTHX_PASSWORD`` and ``MYTHX_ETH_ADDRESS``.
.. code-block:: bash
$ export MYTHX_ETH_ADDRESS='0x0000000000000000000000000000000000000000'
$ export MYTHX_PASSWORD='password'
***********************
Analyzing Solidity Code
***********************
The input format is the same as a regular Mythril analysis.
.. code-block:: bash
$ myth pro ether_send.sol
==== Unprotected Ether Withdrawal ====
SWC ID: 105
Severity: High
Contract: Crowdfunding
Function name: withdrawfunds()
PC address: 730
Anyone can withdraw ETH from the contract account.
Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.
--------------------
In file: tests/testdata/input_contracts/ether_send.sol:21
msg.sender.transfer(address(this).balance)
--------------------
If an input file contains multiple contract definitions, Mythril analyzes the *last* bytecode output produced by solc. You can override this by specifying the contract name explicitly:
.. code-block:: bash
myth pro OmiseGo.sol:OMGToken
To specify a contract address, use :code:`-a <address>`
****************************
Analyzing On-Chain Contracts
****************************
Analyzing a mainnet contract via INFURA:
.. code-block:: bash
myth pro -a 0x5c436ff914c458983414019195e0f4ecbef9e6dd
Adding the :code:`-l` flag will cause mythril to automatically retrieve dependencies, such as dynamically linked library contracts:
.. code-block:: bash
myth -v4 pro -l -a 0xEbFD99838cb0c132016B9E117563CB41f2B02264

@ -1,7 +1,7 @@
Security Analysis
=================
Run :code:`myth -x` with one of the input options described below will run the analysis modules in the `/analysis/modules <https://github.com/ConsenSys/mythril/tree/master/mythril/analysis/modules>`_ directory.
Run :code:`myth analyze` with one of the input options described below will run the analysis modules in the `/analysis/modules <https://github.com/ConsenSys/mythril/tree/master/mythril/analysis/modules>`_ directory.
***********************
Analyzing Solidity Code

@ -4,4 +4,4 @@ This file is suitable for sourcing inside POSIX shell, e.g. bash as well
as for importing into Python.
"""
__version__ = "v0.21.15"
__version__ = "v0.21.16"

@ -6,15 +6,21 @@
- SWC ID: {{ issue['swc-id'] }}
- Severity: {{ issue.severity }}
- Contract: {{ issue.contract | default("Unknown") }}
{% if issue.function %}
- Function name: `{{ issue.function }}`
{% endif %}
- PC address: {{ issue.address }}
{% if issue.min_gas_used or issue.max_gas_used %}
- Estimated Gas Usage: {{ issue.min_gas_used }} - {{ issue.max_gas_used }}
{% endif %}
### Description
{{ issue.description.rstrip() }}
{% if issue.filename and issue.lineno %}
In file: {{ issue.filename }}:{{ issue.lineno }}
{% elif issue.filename %}
In file: {{ issue.filename }}
{% endif %}
{% if issue.code %}

@ -4,9 +4,13 @@
SWC ID: {{ issue['swc-id'] }}
Severity: {{ issue.severity }}
Contract: {{ issue.contract | default("Unknown") }}
{% if issue.function %}
Function name: {{ issue.function }}
{% endif %}
PC address: {{ issue.address }}
{% if issue.min_gas_used or issue.max_gas_used %}
Estimated Gas Usage: {{ issue.min_gas_used }} - {{ issue.max_gas_used }}
{% endif %}
{{ issue.description }}
--------------------
{% if issue.filename and issue.lineno %}

@ -24,39 +24,44 @@ def safe_decode(hex_encoded_string):
return bytes.fromhex(hex_encoded_string)
def get_solc_json(file, solc_binary="solc", solc_args=None):
def get_solc_json(file, solc_binary="solc", solc_settings_json=None):
"""
:param file:
:param solc_binary:
:param solc_args:
:param solc_settings_json:
:return:
"""
cmd = [solc_binary, "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,ast"]
if solc_args:
cmd.extend(solc_args.split())
if not "--allow-paths" in cmd:
cmd.extend(["--allow-paths", "."])
else:
for i, arg in enumerate(cmd):
if arg == "--allow-paths":
cmd[i + 1] += ",."
cmd.append(file)
cmd = [solc_binary, "--standard-json", "--allow-paths", "."]
settings = json.loads(solc_settings_json) if solc_settings_json else {}
settings.update(
{
"outputSelection": {
"*": {
"": ["ast"],
"*": [
"metadata",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
],
}
}
}
)
input_json = json.dumps(
{
"language": "Solidity",
"sources": {file: {"urls": [file]}},
"settings": settings,
}
)
try:
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate(bytes(input_json, "utf8"))
stdout, stderr = p.communicate()
ret = p.returncode
if ret != 0:
raise CompilerError(
"Solc experienced a fatal error (code %d).\n\n%s"
% (ret, stderr.decode("UTF-8"))
)
except FileNotFoundError:
raise CompilerError(
"Compiler not found. Make sure that solc is installed and in PATH, or set the SOLC environment variable."
@ -64,10 +69,15 @@ def get_solc_json(file, solc_binary="solc", solc_args=None):
out = stdout.decode("UTF-8")
if not len(out):
raise CompilerError("Compilation failed.")
result = json.loads(out)
for error in result.get("errors", []):
if error["severity"] == "error":
raise CompilerError(
"Solc experienced a fatal error.\n\n%s" % error["formattedMessage"]
)
return json.loads(out)
return result
def encode_calldata(func_name, arg_types, args):

@ -16,6 +16,7 @@ import traceback
import mythril.support.signatures as sigs
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
from mythril import mythx
from mythril.exceptions import AddressNotFoundError, CriticalError
from mythril.mythril import (
MythrilAnalyzer,
@ -27,12 +28,14 @@ from mythril.__version__ import __version__ as VERSION
ANALYZE_LIST = ("analyze", "a")
DISASSEMBLE_LIST = ("disassemble", "d")
PRO_LIST = ("pro", "p")
log = logging.getLogger(__name__)
COMMAND_LIST = (
ANALYZE_LIST
+ DISASSEMBLE_LIST
+ PRO_LIST
+ (
"read-storage",
"leveldb-search",
@ -41,6 +44,7 @@ COMMAND_LIST = (
"version",
"truffle",
"help",
"pro",
)
)
@ -70,7 +74,27 @@ def exit_with_error(format_, message):
sys.exit()
def get_input_parser() -> ArgumentParser:
def get_runtime_input_parser() -> ArgumentParser:
"""
Returns Parser which handles input
:return: Parser which handles input
"""
parser = ArgumentParser(add_help=False)
parser.add_argument(
"-a",
"--address",
help="pull contract from the blockchain",
metavar="CONTRACT_ADDRESS",
)
parser.add_argument(
"--bin-runtime",
action="store_true",
help="Only when -c or -f is used. Consider the input bytecode as binary runtime code, default being the contract creation bytecode.",
)
return parser
def get_creation_input_parser() -> ArgumentParser:
"""
Returns Parser which handles input
:return: Parser which handles input
@ -89,17 +113,6 @@ def get_input_parser() -> ArgumentParser:
metavar="BYTECODEFILE",
type=argparse.FileType("r"),
)
parser.add_argument(
"-a",
"--address",
help="pull contract from the blockchain",
metavar="CONTRACT_ADDRESS",
)
parser.add_argument(
"--bin-runtime",
action="store_true",
help="Only when -c or -f is used. Consider the input bytecode as binary runtime code, default being the contract creation bytecode.",
)
return parser
@ -144,7 +157,10 @@ def get_utilities_parser() -> ArgumentParser:
:return: Parser which handles utility flags
"""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--solc-args", help="Extra arguments for solc")
parser.add_argument(
"--solc-json",
help="Json for the optional 'settings' parameter of solc's standard-json input",
)
parser.add_argument(
"--solv",
help="specify solidity compiler version. If not present, will try to install it (Experimental)",
@ -158,7 +174,8 @@ def main() -> None:
rpc_parser = get_rpc_parser()
utilities_parser = get_utilities_parser()
input_parser = get_input_parser()
runtime_input_parser = get_runtime_input_parser()
creation_input_parser = get_creation_input_parser()
output_parser = get_output_parser()
parser = argparse.ArgumentParser(
description="Security analysis of Ethereum smart contracts"
@ -172,7 +189,13 @@ def main() -> None:
analyzer_parser = subparsers.add_parser(
ANALYZE_LIST[0],
help="Triggers the analysis of the smart contract",
parents=[rpc_parser, utilities_parser, input_parser, output_parser],
parents=[
rpc_parser,
utilities_parser,
creation_input_parser,
runtime_input_parser,
output_parser,
],
aliases=ANALYZE_LIST[1:],
formatter_class=RawTextHelpFormatter,
)
@ -182,11 +205,25 @@ def main() -> None:
DISASSEMBLE_LIST[0],
help="Disassembles the smart contract",
aliases=DISASSEMBLE_LIST[1:],
parents=[rpc_parser, utilities_parser, input_parser],
parents=[
rpc_parser,
utilities_parser,
creation_input_parser,
runtime_input_parser,
],
formatter_class=RawTextHelpFormatter,
)
create_disassemble_parser(disassemble_parser)
pro_parser = subparsers.add_parser(
PRO_LIST[0],
help="Analyzes input with the MythX API (https://mythx.io)",
aliases=PRO_LIST[1:],
parents=[utilities_parser, creation_input_parser, output_parser],
formatter_class=RawTextHelpFormatter,
)
create_pro_parser(pro_parser)
read_storage_parser = subparsers.add_parser(
"read-storage",
help="Retrieves storage slots from a given address through rpc",
@ -234,6 +271,25 @@ def create_disassemble_parser(parser: ArgumentParser):
)
def create_pro_parser(parser: ArgumentParser):
"""
Modify parser to handle mythx analysis
:param parser:
:return:
"""
parser.add_argument(
"solidity_files",
nargs="*",
help="Inputs file name and contract name. \n"
"usage: file1.sol:OptionalContractName file2.sol file3.sol:OptionalContractName",
)
parser.add_argument(
"--full",
help="Run a full analysis. Default: quick analysis",
action="store_true",
)
def create_read_storage_parser(read_storage_parser: ArgumentParser):
"""
Modify parser to handle storage slots
@ -564,6 +620,17 @@ def execute_command(
)
print(storage)
elif args.command in PRO_LIST:
mode = "full" if args.full else "quick"
report = mythx.analyze(disassembler.contracts, mode)
outputs = {
"json": report.as_json(),
"jsonv2": report.as_swc_standard_format(),
"text": report.as_text(),
"markdown": report.as_markdown(),
}
print(outputs[args.outform])
elif args.command in DISASSEMBLE_LIST:
if disassembler.contracts[0].code:
print("Runtime Disassembly: \n" + disassembler.contracts[0].get_easm())
@ -694,12 +761,12 @@ def parse_args_and_execute(parser: ArgumentParser, args: Namespace) -> None:
config = set_config(args)
leveldb_search(config, args)
query_signature = args.__dict__.get("query_signature", None)
solc_args = args.__dict__.get("solc_args", None)
solc_json = args.__dict__.get("solc_json", None)
solv = args.__dict__.get("solv", None)
disassembler = MythrilDisassembler(
eth=config.eth,
solc_version=solv,
solc_args=solc_args,
solc_settings_json=solc_json,
enable_online_lookup=query_signature,
)
if args.command == "truffle":

@ -229,7 +229,10 @@ def create_parser(parser: argparse.ArgumentParser) -> None:
default=10,
help="The amount of seconds to spend on " "the initial contract creation",
)
options.add_argument("--solc-args", help="Extra arguments for solc")
options.add_argument(
"--solc-json",
help="Json for the optional 'settings' parameter of solc's standard-json input",
)
options.add_argument(
"--phrack", action="store_true", help="Phrack-style call graph"
)
@ -522,7 +525,7 @@ def parse_args(parser: argparse.ArgumentParser, args: argparse.Namespace) -> Non
disassembler = MythrilDisassembler(
eth=config.eth,
solc_version=args.solv,
solc_args=args.solc_args,
solc_settings_json=args.solc_json,
enable_online_lookup=args.query_signature,
)
if args.truffle:

@ -45,7 +45,7 @@ def get_instruction_index(
"""
index = 0
for instr in instruction_list:
if instr["address"] == address:
if instr["address"] >= address:
return index
index += 1
return None

@ -30,11 +30,11 @@ class MythrilDisassembler:
self,
eth: Optional[EthJsonRpc] = None,
solc_version: str = None,
solc_args: str = None,
solc_settings_json: str = None,
enable_online_lookup: bool = False,
) -> None:
self.solc_binary = self._init_solc_binary(solc_version)
self.solc_args = solc_args
self.solc_settings_json = solc_settings_json
self.eth = eth
self.enable_online_lookup = enable_online_lookup
self.sigs = signatures.SignatureDB(enable_online_lookup=enable_online_lookup)
@ -163,13 +163,15 @@ class MythrilDisassembler:
try:
# import signatures from solidity source
self.sigs.import_solidity_file(
file, solc_binary=self.solc_binary, solc_args=self.solc_args
file,
solc_binary=self.solc_binary,
solc_settings_json=self.solc_settings_json,
)
if contract_name is not None:
contract = SolidityContract(
input_file=file,
name=contract_name,
solc_args=self.solc_args,
solc_settings_json=self.solc_settings_json,
solc_binary=self.solc_binary,
)
self.contracts.append(contract)
@ -177,7 +179,7 @@ class MythrilDisassembler:
else:
for contract in get_contracts_from_file(
input_file=file,
solc_args=self.solc_args,
solc_settings_json=self.solc_settings_json,
solc_binary=self.solc_binary,
):
self.contracts.append(contract)

@ -0,0 +1,111 @@
import sys
import os
import time
from mythx_models.exceptions import MythXAPIError
from typing import List, Dict, Any
from mythril.analysis.report import Issue, Report
from mythril.solidity.soliditycontract import SolidityContract
from pythx import Client
import logging
log = logging.getLogger(__name__)
TRIAL_ETH_ADDRESS = "0x0000000000000000000000000000000000000000"
TRIAL_PASSWORD = "trial"
def analyze(contracts: List[SolidityContract], analysis_mode: str = "quick") -> Report:
"""
Analyze contracts via the MythX API.
:param contracts: List of solidity contracts to analyze
:param analysis_mode: The mode to submit the analysis request with. "quick" or "full" (default: "quick")
:return: Report with analyzed contracts
"""
assert analysis_mode in ("quick", "full"), "analysis_mode must be 'quick' or 'full'"
c = Client(
eth_address=os.environ.get("MYTHX_ETH_ADDRESS", TRIAL_ETH_ADDRESS),
password=os.environ.get("MYTHX_PASSWORD", TRIAL_PASSWORD),
)
if c.eth_address == TRIAL_ETH_ADDRESS:
print(
"You are currently running MythX in Trial mode. This mode reports only a partial analysis of your smart contracts, limited to three vulnerabilities. To get a more complete analysis, sign up for a free account at https://mythx.io."
)
issues = [] # type: List[Issue]
# TODO: Analyze multiple contracts asynchronously.
for contract in contracts:
source_codes = {}
source_list = []
sources = {} # type: Dict[str, Any]
main_source = None
try:
main_source = contract.input_file
for solidity_file in contract.solidity_files:
source_codes[solidity_file.filename] = solidity_file.data
for filename in contract.solc_json["sources"].keys():
sources[filename] = {}
if source_codes[filename]:
sources[filename]["source"] = source_codes[filename]
sources[filename]["ast"] = contract.solc_json["sources"][filename][
"ast"
]
source_list.append(filename)
source_list.sort(
key=lambda fname: contract.solc_json["sources"][fname]["id"]
)
except AttributeError:
# No solidity file
pass
assert contract.creation_code, "Creation bytecode must exist."
try:
resp = c.analyze(
contract_name=contract.name,
analysis_mode=analysis_mode,
bytecode=contract.creation_code or None,
deployed_bytecode=contract.code or None,
sources=sources or None,
main_source=main_source,
source_list=source_list or None,
)
except MythXAPIError as e:
log.critical(e)
while not c.analysis_ready(resp.uuid):
log.info(c.status(resp.uuid).analysis)
time.sleep(5)
for issue in c.report(resp.uuid):
issue = Issue(
contract=contract.name,
function_name=None,
address=issue.locations[0].source_map.components[0].offset
if issue.locations
else -1,
swc_id=issue.swc_id[4:] or "None", # remove 'SWC-' prefix
title=issue.swc_title,
bytecode=contract.creation_code,
severity=issue.severity.capitalize(),
description_head=issue.description_short,
description_tail=issue.description_long,
)
issue.add_code_info(contract)
issues.append(issue)
report = Report(contracts=contracts)
for issue in issues:
report.append_issue(issue)
return report

@ -44,23 +44,28 @@ class SourceCodeInfo:
self.solc_mapping = mapping
def get_contracts_from_file(input_file, solc_args=None, solc_binary="solc"):
def get_contracts_from_file(input_file, solc_settings_json=None, solc_binary="solc"):
"""
:param input_file:
:param solc_args:
:param solc_settings_json:
:param solc_binary:
"""
data = get_solc_json(input_file, solc_args=solc_args, solc_binary=solc_binary)
data = get_solc_json(
input_file, solc_settings_json=solc_settings_json, solc_binary=solc_binary
)
try:
for key, contract in data["contracts"].items():
filename, name = key.split(":")
if filename == input_file and len(contract["bin-runtime"]):
for contract_name in data["contracts"][input_file].keys():
if len(
data["contracts"][input_file][contract_name]["evm"]["deployedBytecode"][
"object"
]
):
yield SolidityContract(
input_file=input_file,
name=name,
solc_args=solc_args,
name=contract_name,
solc_settings_json=solc_settings_json,
solc_binary=solc_binary,
)
except KeyError:
@ -70,16 +75,22 @@ def get_contracts_from_file(input_file, solc_args=None, solc_binary="solc"):
class SolidityContract(EVMContract):
"""Representation of a Solidity contract."""
def __init__(self, input_file, name=None, solc_args=None, solc_binary="solc"):
data = get_solc_json(input_file, solc_args=solc_args, solc_binary=solc_binary)
def __init__(
self, input_file, name=None, solc_settings_json=None, solc_binary="solc"
):
data = get_solc_json(
input_file, solc_settings_json=solc_settings_json, solc_binary=solc_binary
)
self.solidity_files = []
self.solc_json = data
self.input_file = input_file
for filename in data["sourceList"]:
for filename, contract in data["sources"].items():
with open(filename, "r", encoding="utf-8") as file:
code = file.read()
full_contract_src_maps = self.get_full_contract_src_maps(
data["sources"][filename]["AST"]
contract["ast"]
)
self.solidity_files.append(
SolidityFile(filename, code, full_contract_src_maps)
@ -91,32 +102,28 @@ class SolidityContract(EVMContract):
srcmap_constructor = []
srcmap = []
if name:
for key, contract in sorted(data["contracts"].items()):
filename, _name = key.split(":")
if (
filename == input_file
and name == _name
and len(contract["bin-runtime"])
):
code = contract["bin-runtime"]
creation_code = contract["bin"]
srcmap = contract["srcmap-runtime"].split(";")
srcmap_constructor = contract["srcmap"].split(";")
contract = data["contracts"][input_file][name]
if len(contract["evm"]["deployedBytecode"]["object"]):
code = contract["evm"]["deployedBytecode"]["object"]
creation_code = contract["evm"]["bytecode"]["object"]
srcmap = contract["evm"]["deployedBytecode"]["sourceMap"].split(";")
srcmap_constructor = contract["evm"]["bytecode"]["sourceMap"].split(";")
has_contract = True
break
# If no contract name is specified, get the last bytecode entry for the input file
else:
for key, contract in sorted(data["contracts"].items()):
filename, name = key.split(":")
if filename == input_file and len(contract["bin-runtime"]):
code = contract["bin-runtime"]
creation_code = contract["bin"]
srcmap = contract["srcmap-runtime"].split(";")
srcmap_constructor = contract["srcmap"].split(";")
for contract_name, contract in sorted(
data["contracts"][input_file].items()
):
if len(contract["evm"]["deployedBytecode"]["object"]):
name = contract_name
code = contract["evm"]["deployedBytecode"]["object"]
creation_code = contract["evm"]["bytecode"]["object"]
srcmap = contract["evm"]["deployedBytecode"]["sourceMap"].split(";")
srcmap_constructor = contract["evm"]["bytecode"]["sourceMap"].split(
";"
)
has_contract = True
if not has_contract:
@ -139,8 +146,8 @@ class SolidityContract(EVMContract):
:return: The source maps
"""
source_maps = set()
for child in ast["children"]:
if "contractKind" in child["attributes"]:
for child in ast["nodes"]:
if child.get("contractKind"):
source_maps.add(child["src"])
return source_maps

@ -1,5 +1,6 @@
"""The Mythril function signature database."""
import functools
import json
import logging
import multiprocessing
import os
@ -9,6 +10,7 @@ from collections import defaultdict
from subprocess import PIPE, Popen
from typing import List, Set, DefaultDict, Dict
from mythril.ethereum.util import get_solc_json
from mythril.exceptions import CompilerError
log = logging.getLogger(__name__)
@ -231,53 +233,20 @@ class SignatureDB(object, metaclass=Singleton):
return []
def import_solidity_file(
self, file_path: str, solc_binary: str = "solc", solc_args: str = None
self, file_path: str, solc_binary: str = "solc", solc_settings_json: str = None
):
"""Import Function Signatures from solidity source files.
:param solc_binary:
:param solc_args:
:param solc_settings_json:
:param file_path: solidity source code file path
:return:
"""
cmd = [solc_binary, "--hashes", file_path]
if solc_args:
cmd.extend(solc_args.split())
solc_json = get_solc_json(file_path, solc_binary, solc_settings_json)
try:
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
ret = p.returncode
if ret != 0:
raise CompilerError(
"Solc has experienced a fatal error (code {}).\n\n{}".format(
ret, stderr.decode("utf-8")
)
)
except FileNotFoundError:
raise CompilerError(
(
"Compiler not found. Make sure that solc is installed and in PATH, "
"or the SOLC environment variable is set."
)
)
stdout = stdout.decode("unicode_escape").split("\n")
for line in stdout:
# the ':' need not be checked but just to be sure
if all(map(lambda x: x in line, ["(", ")", ":"])):
solc_bytes = "0x" + line.split(":")[0]
solc_text = line.split(":")[1].strip()
self.solidity_sigs[solc_bytes].append(solc_text)
log.debug(
"Signatures: found %d signatures after parsing" % len(self.solidity_sigs)
)
# update DB with what we've found
for byte_sig, text_sigs in self.solidity_sigs.items():
for text_sig in text_sigs:
self.add(byte_sig, text_sig)
for contract in solc_json["contracts"][file_path].values():
for name, hash in contract["evm"]["methodIdentifiers"].items():
self.add("0x" + hash, name)
@staticmethod
def lookup_online(byte_sig: str, timeout: int, proxies=None) -> List[str]:

@ -21,9 +21,10 @@ py-solc
pytest>=3.6.0
pytest-cov
pytest_mock
requests
requests>=2.22.0
rlp>=1.0.1
transaction>=2.2.1
z3-solver>=4.8.5.0
pysha3
matplotlib
pythx

@ -28,7 +28,7 @@ REQUIRED = [
"py_ecc==1.6.0",
"ethereum>=2.3.2",
"z3-solver>=4.8.5.0",
"requests",
"requests>=2.22.0",
"py-solc",
"plyvel",
"eth_abi==1.3.0",
@ -49,6 +49,7 @@ REQUIRED = [
"persistent>=4.2.0",
"ethereum-input-decoder>=0.2.2",
"matplotlib",
"pythx",
]
TESTS_REQUIRE = ["mypy", "pytest>=3.6.0", "pytest_mock", "pytest-cov"]

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save