|
|
|
@ -1,5 +1,7 @@ |
|
|
|
|
"""This module contains representation classes for Solidity files, contracts |
|
|
|
|
and source mappings.""" |
|
|
|
|
from typing import Dict, Set |
|
|
|
|
|
|
|
|
|
import mythril.laser.ethereum.util as helper |
|
|
|
|
from mythril.ethereum.evmcontract import EVMContract |
|
|
|
|
from mythril.ethereum.util import get_solc_json |
|
|
|
@ -20,9 +22,16 @@ class SourceMapping: |
|
|
|
|
class SolidityFile: |
|
|
|
|
"""Representation of a file containing Solidity code.""" |
|
|
|
|
|
|
|
|
|
def __init__(self, filename, data): |
|
|
|
|
def __init__(self, filename: str, data: str, full_contract_source: Set[str]): |
|
|
|
|
""" |
|
|
|
|
Metadata class containing data regarding a specific solidity file |
|
|
|
|
:param filename: The filename of the solidity file |
|
|
|
|
:param data: The code of the solidity file |
|
|
|
|
:param full_contract_source: The set of contract source mappings of all the contracts in the file |
|
|
|
|
""" |
|
|
|
|
self.filename = filename |
|
|
|
|
self.data = data |
|
|
|
|
self.full_contract_source = full_contract_source |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SourceCodeInfo: |
|
|
|
@ -69,7 +78,12 @@ class SolidityContract(EVMContract): |
|
|
|
|
for filename in data["sourceList"]: |
|
|
|
|
with open(filename, "r", encoding="utf-8") as file: |
|
|
|
|
code = file.read() |
|
|
|
|
self.solidity_files.append(SolidityFile(filename, code)) |
|
|
|
|
full_contract_sources = self.get_full_contract_sources( |
|
|
|
|
data["sources"][filename]["AST"] |
|
|
|
|
) |
|
|
|
|
self.solidity_files.append( |
|
|
|
|
SolidityFile(filename, code, full_contract_sources) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
has_contract = False |
|
|
|
|
|
|
|
|
@ -117,6 +131,19 @@ class SolidityContract(EVMContract): |
|
|
|
|
|
|
|
|
|
super().__init__(code, creation_code, name=name) |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def get_full_contract_sources(ast: Dict) -> Set[str]: |
|
|
|
|
""" |
|
|
|
|
Takes a solc AST and gets the src mappings for all the contracts defined in the top level of the ast |
|
|
|
|
:param ast: AST of the contract |
|
|
|
|
:return: The source map |
|
|
|
|
""" |
|
|
|
|
source_map = set() |
|
|
|
|
for child in ast["children"]: |
|
|
|
|
if "contractKind" in child["attributes"]: |
|
|
|
|
source_map.add(child["src"]) |
|
|
|
|
return source_map |
|
|
|
|
|
|
|
|
|
def get_source_info(self, address, constructor=False): |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
@ -140,6 +167,26 @@ class SolidityContract(EVMContract): |
|
|
|
|
lineno = mappings[index].lineno |
|
|
|
|
return SourceCodeInfo(filename, lineno, code, mappings[index].solc_mapping) |
|
|
|
|
|
|
|
|
|
def _is_autogenerated_code(self, offset: int, length: int, file_index: int) -> bool: |
|
|
|
|
""" |
|
|
|
|
Checks whether the code is autogenerated or not |
|
|
|
|
:param offset: offset of the code |
|
|
|
|
:param length: length of the code |
|
|
|
|
:param file_index: file the code corresponds to |
|
|
|
|
:return: True if the code is internally generated, else false |
|
|
|
|
""" |
|
|
|
|
# Handle internal compiler files |
|
|
|
|
if file_index == -1: |
|
|
|
|
return True |
|
|
|
|
# Handle the common code src map for the entire code. |
|
|
|
|
if ( |
|
|
|
|
"{}:{}:{}".format(offset, length, file_index) |
|
|
|
|
in self.solidity_files[file_index].full_contract_source |
|
|
|
|
): |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def _get_solc_mappings(self, srcmap, constructor=False): |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
@ -161,7 +208,8 @@ class SolidityContract(EVMContract): |
|
|
|
|
|
|
|
|
|
if len(mapping) > 2 and len(mapping[2]) > 0: |
|
|
|
|
idx = int(mapping[2]) |
|
|
|
|
if idx == -1: |
|
|
|
|
|
|
|
|
|
if self._is_autogenerated_code(offset, length, idx): |
|
|
|
|
lineno = None |
|
|
|
|
else: |
|
|
|
|
lineno = ( |
|
|
|
|