address mypy warnings

pull/1311/head
alpharush 2 years ago
parent 14ae013c9a
commit 5669dc5cd4
  1. 4
      slither/core/solidity_types/elementary_type.py
  2. 109
      slither/tools/read_storage/read_storage.py
  3. 24
      slither/tools/read_storage/utils/utils.py

@ -172,7 +172,7 @@ class ElementaryType(Type):
return self.type
@property
def size(self) -> Optional[int]:
def size(self) -> int:
"""
Return the size in bits
Return None if the size is not known
@ -190,7 +190,7 @@ class ElementaryType(Type):
return int(160)
if t.startswith("bytes") and t != "bytes":
return int(t[len("bytes") :]) * 8
return None
raise SlitherException(f"{t} does not have a size")
@property
def storage_size(self) -> Tuple[int, bool]:

@ -2,7 +2,7 @@ import logging
import sys
from math import floor
from os import environ
from typing import Callable, Optional, Tuple, Union, List, Dict, Any
from typing import Callable, Optional, Tuple, Union, List, Dict, Any, NewType
try:
from web3 import Web3
@ -20,12 +20,12 @@ except ImportError:
print("$ pip3 install web3 --user\n")
sys.exit(-1)
try:
from tabulate import tabulate
except ImportError:
print("ERROR: in order to use slither-read-storage --table, you need to install tabulate")
print("$ pip3 install tabulate --user\n")
sys.exit(-1)
# try:
# from tabulate import tabulate
# except ImportError:
# print("ERROR: in order to use slither-read-storage --table, you need to install tabulate")
# print("$ pip3 install tabulate --user\n")
# sys.exit(-1)
from dataclasses import dataclass, field
@ -39,7 +39,8 @@ logging.basicConfig()
logger = logging.getLogger("Slither-read-storage")
logger.setLevel(logging.INFO)
Elem = NewType("Elem", Dict[str, "SlotInfo"])
NestedElem = NewType("NestedElem", Dict[str, Elem])
@dataclass
class SlotInfo:
type_string: str
@ -48,7 +49,7 @@ class SlotInfo:
offset: int
value: Optional[Union[int, bool, str, ChecksumAddress]] = None
# For structure, str->SlotInfo, for array, str-> SlotInfo
elems: Dict[str, "SlotInfo"] = field(default_factory=lambda: {})
elems: Elem = field(default_factory=lambda: {})
struct_var: Optional[str] = None
@ -109,7 +110,7 @@ class SlitherReadStorage:
def get_storage_layout(self) -> None:
"""Retrieves the storage layout of entire contract."""
tmp: Dict[str, SlotInfo] = {}
tmp: Elem
for contract, var in self.target_variables:
type_ = var.type
info = self.get_storage_slot(var, contract)
@ -186,7 +187,7 @@ class SlitherReadStorage:
elif isinstance(target_variable_type, MappingType) and key:
info, type_to, slot, size, offset = self._find_mapping_slot(
target_variable, slot, key, struct_var=struct_var, deep_key=deep_key
target_variable_type, slot, key, struct_var=struct_var, deep_key=deep_key
)
self.log += info
@ -220,9 +221,11 @@ class SlitherReadStorage:
struct_var (str): Structure variable name.
"""
for contract, var in self.target_variables:
self._slot_info[f"{contract.name}.{var.name}"] = self.get_storage_slot(
slot_info = self.get_storage_slot(
var, contract, **kwargs
)
if slot_info:
self._slot_info[f"{contract.name}.{var.name}"] = slot_info
def get_slot_values(self) -> None:
"""
@ -425,7 +428,6 @@ class SlitherReadStorage:
slot = int.from_bytes(slot_as_bytes, "big")
offset = 0
type_to = ""
size = 0
for var in elems:
var_type = var.type
if isinstance(var_type, ElementaryType):
@ -477,6 +479,8 @@ class SlitherReadStorage:
if isinstance(
target_variable_type_type, ArrayType
): # multidimensional array uint[i][], , uint[][i], or uint[][]
assert isinstance(target_variable_type_type.type, ElementaryType)
assert target_variable_type_type.type.size
size = target_variable_type_type.type.size
type_to = target_variable_type_type.type.name
@ -503,7 +507,7 @@ class SlitherReadStorage:
elif target_variable_type.is_fixed_array:
slot_int = int.from_bytes(slot, "big") + int(key)
if isinstance(target_variable_type_type, UserDefinedType): # struct[i]
if isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[i]
type_to = target_variable_type_type.type.name
if not struct_var:
return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset
@ -515,10 +519,12 @@ class SlitherReadStorage:
info += info_tmp
else:
assert isinstance(target_variable_type_type, ElementaryType)
type_to = target_variable_type_type.name
assert target_variable_type_type.size
size = target_variable_type_type.size # bits
elif isinstance(target_variable_type_type, UserDefinedType): # struct[]
elif isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[]
slot = keccak(slot)
slot_int = int.from_bytes(slot, "big") + int(key)
type_to = target_variable_type_type.type.name
@ -532,6 +538,7 @@ class SlitherReadStorage:
info += info_tmp
else:
assert isinstance(target_variable_type_type, ElementaryType) and target_variable_type_type.size
slot = keccak(slot)
slot_int = int.from_bytes(slot, "big") + int(key)
type_to = target_variable_type_type.name
@ -543,7 +550,7 @@ class SlitherReadStorage:
@staticmethod
def _find_mapping_slot(
target_variable: StateVariable,
target_variable_type: MappingType,
slot: bytes,
key: Union[int, str],
deep_key: Union[int, str] = None,
@ -557,7 +564,7 @@ class SlitherReadStorage:
struct_var (str, optional): Structure variable name.
:returns:
log (str): Info about the target variable to log.
type_to (bytes): The type of the target variable.
type_to (str): The type of the target variable.
slot (bytes): The storage location of the target variable.
size (int): The size (in bits) of the target variable.
offset (int): The size of other variables that share the same slot.
@ -569,29 +576,30 @@ class SlitherReadStorage:
info += f"\nKey: {key}"
if deep_key:
info += f"\nDeep Key: {deep_key}"
key_type = target_variable.type.type_from.name
assert isinstance(target_variable_type.type_from, ElementaryType)
key_type = target_variable_type.type_from.name
assert key
if "int" in key_type: # without this eth_utils encoding fails
key = int(key)
key = coerce_type(key_type, key)
slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)]))
if isinstance(target_variable.type.type_to, UserDefinedType) and isinstance(
target_variable.type.type_to.type, Structure
if isinstance(target_variable_type.type_to, UserDefinedType) and isinstance(
target_variable_type.type_to.type, Structure
): # mapping(elem => struct)
assert struct_var
elems = target_variable.type.type_to.type.elems_ordered
elems = target_variable_type.type_to.type.elems_ordered
info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot(
elems, slot, struct_var
)
info += info_tmp
elif isinstance(
target_variable.type.type_to, MappingType
target_variable_type.type_to, MappingType
): # mapping(elem => mapping(elem => ???))
assert deep_key
key_type = target_variable.type.type_to.type_from.name
assert isinstance(target_variable_type.type_to.type_from, ElementaryType)
key_type = target_variable_type.type_to.type_from.name
if "int" in key_type: # without this eth_utils encoding fails
deep_key = int(deep_key)
@ -599,16 +607,18 @@ class SlitherReadStorage:
slot = keccak(encode_abi([key_type, "bytes32"], [deep_key, slot]))
# mapping(elem => mapping(elem => elem))
type_to = target_variable.type.type_to.type_to.type
byte_size, _ = target_variable.type.type_to.type_to.storage_size
target_variable_type_type_to_type_to = target_variable_type.type_to.type_to
assert isinstance(target_variable_type_type_to_type_to, (UserDefinedType, ElementaryType))
type_to = str(target_variable_type_type_to_type_to.type)
byte_size, _ = target_variable_type_type_to_type_to.storage_size
size = byte_size * 8 # bits
offset = 0
if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and isinstance(
target_variable.type.type_to.type_to.type, Structure
if isinstance(target_variable_type_type_to_type_to, UserDefinedType) and isinstance(
target_variable_type_type_to_type_to.type, Structure
): # mapping(elem => mapping(elem => struct))
assert struct_var
elems = target_variable.type.type_to.type_to.type.elems_ordered
elems = target_variable_type_type_to_type_to.type.elems_ordered
# If map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth);
info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot(
elems, slot, struct_var
@ -617,11 +627,15 @@ class SlitherReadStorage:
# TODO: suppory mapping with dynamic arrays
else: # mapping(elem => elem)
type_to = target_variable.type.type_to.name # the value's elementary type
byte_size, _ = target_variable.type.type_to.storage_size
# mapping(elem => elem)
elif isinstance(target_variable_type.type_to, ElementaryType):
type_to = target_variable_type.type_to.name # the value's elementary type
byte_size, _ = target_variable_type.type_to.storage_size
size = byte_size * 8 # bits
else:
raise NotImplementedError(f"{target_variable_type} => {target_variable_type.type_to} not implemented")
return info, type_to, slot, size, offset
@staticmethod
@ -629,6 +643,7 @@ class SlitherReadStorage:
contract: Contract, target_variable: StateVariable
) -> Tuple[int, int, int, str]:
"""Return slot, size, offset, and type."""
assert isinstance(target_variable.type, Type)
type_to = str(target_variable.type)
byte_size, _ = target_variable.type.storage_size
size = byte_size * 8 # bits
@ -643,7 +658,7 @@ class SlitherReadStorage:
@staticmethod
def convert_value_to_type(
hex_bytes: HexBytes, size: int, offset: int, type_to: str
hex_bytes: bytes, size: int, offset: int, type_to: str
) -> Union[int, bool, str, ChecksumAddress]:
"""Convert slot data to type representation."""
# Account for storage packing
@ -657,10 +672,10 @@ class SlitherReadStorage:
def _all_struct_slots(
self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None
) -> Dict[str, SlotInfo]:
) -> Elem:
"""Retrieves all members of a struct."""
struct_elems = st.elems_ordered
data: Dict[str, SlotInfo] = {}
data: Elem
for elem in struct_elems:
info = self.get_storage_slot(
var,
@ -673,21 +688,18 @@ class SlitherReadStorage:
return data
# TODO: remove this exception (montyly)
# pylint: disable=too-many-nested-blocks
def _all_array_slots(
self, var: StateVariable, contract: Contract, type_: Type, slot: int
) -> Dict[str, SlotInfo]:
self, var: StateVariable, contract: Contract, type_: ArrayType, slot: int
) -> Elem:
"""Retrieves all members of an array."""
array_length = self._get_array_length(type_, slot)
elems: Dict[str, SlotInfo] = {}
elems: Elem
tartget_variable_type = type_.type
if isinstance(type_, UserDefinedType):
st = type_.type
if isinstance(st, Structure):
if isinstance(tartget_variable_type, Structure):
for i in range(min(array_length, self.max_depth)):
# TODO: figure out why _all_struct_slots returns a Dict[str, SlotInfo]
# but this expect a SlotInfo (montyly)
elems[str(i)] = self._all_struct_slots(var, st, contract, key=i)
elems[str(i)] = self._all_struct_slots(var, tartget_variable_type, contract, key=i)
else:
for i in range(min(array_length, self.max_depth)):
@ -699,8 +711,8 @@ class SlitherReadStorage:
if info:
elems[str(i)] = info
if isinstance(type_.type, ArrayType): # multidimensional array
array_length = self._get_array_length(type_.type, info.slot)
if isinstance(tartget_variable_type, ArrayType): # multidimensional array
array_length = self._get_array_length(tartget_variable_type, info.slot)
for j in range(min(array_length, self.max_depth)):
info = self.get_storage_slot(
@ -710,13 +722,14 @@ class SlitherReadStorage:
deep_key=str(j),
)
if info:
elems[str(i)].elems[str(j)] = info
elem = elems[str(i)]
elem.elems[str(j)] = info
return elems
def _get_array_length(self, type_: Type, slot: int) -> int:
"""Gets the length of dynamic and fixed arrays.
Args:
type_ (`Type`): The array type.
type_ (`AbstractType`): The array type.
slot (int): Slot a dynamic array's length is stored at.
Returns:
(int): The length of the array.

@ -2,10 +2,9 @@ from typing import Union
from eth_typing.evm import ChecksumAddress
from eth_utils import to_int, to_text, to_checksum_address
from hexbytes import HexBytes
def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes:
def get_offset_value(hex_bytes: bytes, offset: int, size: int) -> bytes:
"""
Trims slot data to only contain the target variable's.
Args:
@ -25,7 +24,7 @@ def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes:
return value
def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, ChecksumAddress]:
def coerce_type(solidity_type: str, value: Union[int, str, bytes]) -> Union[int, bool, str, ChecksumAddress]:
"""
Converts input to the indicated type.
Args:
@ -35,24 +34,27 @@ def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, Check
(Union[int, bool, str, ChecksumAddress, hex]): The type representation of the value.
"""
if "int" in solidity_type:
converted_value = to_int(value)
return to_int(value)
elif "bool" in solidity_type:
converted_value = bool(to_int(value))
elif "string" in solidity_type:
return bool(to_int(value))
elif "string" in solidity_type and isinstance(value, bytes):
# length * 2 is stored in lower end bits
# TODO handle bytes and strings greater than 32 bytes
length = int(int.from_bytes(value[-2:], "big") / 2)
converted_value = to_text(value[:length])
return to_text(value[:length])
elif "address" in solidity_type:
converted_value = to_checksum_address(value)
if not isinstance(value, (str, bytes)):
raise TypeError
return to_checksum_address(value)
else:
converted_value = value.hex()
if not isinstance(value, bytes):
raise TypeError
return value.hex()
return converted_value
def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> HexBytes:
def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> bytes:
"""
Retrieves the storage data from the blockchain at target address and slot.
Args:

Loading…
Cancel
Save