|
|
|
@ -1,6 +1,7 @@ |
|
|
|
|
import sys |
|
|
|
|
import logging |
|
|
|
|
from math import floor |
|
|
|
|
from os import environ |
|
|
|
|
|
|
|
|
|
from typing import Callable, Optional, Tuple, Union, List, Dict |
|
|
|
|
|
|
|
|
@ -31,6 +32,13 @@ 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) |
|
|
|
|
|
|
|
|
|
from slither.core.solidity_types.type import Type |
|
|
|
|
from slither.core.solidity_types import ArrayType |
|
|
|
|
from slither.core.declarations import Contract, StructureContract |
|
|
|
@ -137,7 +145,8 @@ class SlitherReadStorage: |
|
|
|
|
contracts (`Contract`): The contract that contains the given state variable. |
|
|
|
|
**kwargs: |
|
|
|
|
key (str): Key of a mapping or index position if an array. |
|
|
|
|
deep_key (str): Key of a mapping embedded within another mapping or secondary index if array. |
|
|
|
|
deep_key (str): Key of a mapping embedded within another mapping or |
|
|
|
|
secondary index if array. |
|
|
|
|
struct_var (str): Structure variable name. |
|
|
|
|
Returns: |
|
|
|
|
(`SlotInfo`) | None : A dictionary of the slot information. |
|
|
|
@ -182,13 +191,22 @@ class SlitherReadStorage: |
|
|
|
|
|
|
|
|
|
int_slot = int.from_bytes(slot, byteorder="big") |
|
|
|
|
self.log += f"\nName: {var_log_name}\nType: {type_to}\nSlot: {int_slot}\n" |
|
|
|
|
logger.info(self.log) |
|
|
|
|
if environ.get("SILENT") is None: |
|
|
|
|
logger.info(self.log) |
|
|
|
|
self.log = "" |
|
|
|
|
if environ.get("TABLE") is None: |
|
|
|
|
return { |
|
|
|
|
"type_string": type_to, |
|
|
|
|
"slot": int_slot, |
|
|
|
|
"size": size, |
|
|
|
|
"offset": offset, |
|
|
|
|
} |
|
|
|
|
return { |
|
|
|
|
"type_string": type_to, |
|
|
|
|
"slot": int_slot, |
|
|
|
|
"size": size, |
|
|
|
|
"offset": offset, |
|
|
|
|
"struct_var": struct_var, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
def get_target_variables(self, **kwargs) -> None: |
|
|
|
@ -241,6 +259,153 @@ class SlitherReadStorage: |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
def print_table(self) -> None: |
|
|
|
|
|
|
|
|
|
tabulate_data = [] |
|
|
|
|
|
|
|
|
|
for _, state_var in self.target_variables: |
|
|
|
|
type_ = state_var.type |
|
|
|
|
var = state_var.name |
|
|
|
|
info = self.slot_info[var] |
|
|
|
|
slot = info.get("slot") |
|
|
|
|
offset = info.get("offset") |
|
|
|
|
size = info.get("size") |
|
|
|
|
type_string = info.get("type_string") |
|
|
|
|
struct_var = info.get("struct_var") |
|
|
|
|
|
|
|
|
|
tabulate_data.append([slot, offset, size, type_string, var]) |
|
|
|
|
|
|
|
|
|
if is_user_defined_type(type_) and is_struct(type_.type): |
|
|
|
|
tabulate_data.pop() |
|
|
|
|
for item in info["elems"]: |
|
|
|
|
slot = info["elems"][item].get("slot") |
|
|
|
|
offset = info["elems"][item].get("offset") |
|
|
|
|
size = info["elems"][item].get("size") |
|
|
|
|
type_string = info["elems"][item].get("type_string") |
|
|
|
|
struct_var = info["elems"][item].get("struct_var") |
|
|
|
|
|
|
|
|
|
# doesn't handle deep keys currently |
|
|
|
|
var_name_struct_or_array_var = f"{var} -> {struct_var}" |
|
|
|
|
|
|
|
|
|
tabulate_data.append( |
|
|
|
|
[slot, offset, size, type_string, var_name_struct_or_array_var] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if is_array(type_): |
|
|
|
|
tabulate_data.pop() |
|
|
|
|
for item in info["elems"]: |
|
|
|
|
for key in info["elems"][item]: |
|
|
|
|
slot = info["elems"][item][key].get("slot") |
|
|
|
|
offset = info["elems"][item][key].get("offset") |
|
|
|
|
size = info["elems"][item][key].get("size") |
|
|
|
|
type_string = info["elems"][item][key].get("type_string") |
|
|
|
|
struct_var = info["elems"][item][key].get("struct_var") |
|
|
|
|
|
|
|
|
|
# doesn't handle deep keys currently |
|
|
|
|
var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" |
|
|
|
|
|
|
|
|
|
tabulate_data.append( |
|
|
|
|
[slot, offset, size, type_string, var_name_struct_or_array_var] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
print( |
|
|
|
|
tabulate( |
|
|
|
|
tabulate_data, headers=["slot", "offset", "size", "type", "name"], tablefmt="grid" |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
def print_table_with_values(self) -> None: |
|
|
|
|
|
|
|
|
|
print("Processing, grabbing values from rpc endpoint...") |
|
|
|
|
tabulate_data = [] |
|
|
|
|
|
|
|
|
|
for _, state_var in self.target_variables: |
|
|
|
|
type_ = state_var.type |
|
|
|
|
var = state_var.name |
|
|
|
|
info = self.slot_info[var] |
|
|
|
|
slot = info.get("slot") |
|
|
|
|
offset = info.get("offset") |
|
|
|
|
size = info.get("size") |
|
|
|
|
type_string = info.get("type_string") |
|
|
|
|
struct_var = info.get("struct_var") |
|
|
|
|
|
|
|
|
|
tabulate_data.append( |
|
|
|
|
[ |
|
|
|
|
slot, |
|
|
|
|
offset, |
|
|
|
|
size, |
|
|
|
|
type_string, |
|
|
|
|
var, |
|
|
|
|
self.convert_value_to_type( |
|
|
|
|
get_storage_data(self.web3, self.checksum_address, slot), |
|
|
|
|
size, |
|
|
|
|
offset, |
|
|
|
|
type_string, |
|
|
|
|
), |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if is_user_defined_type(type_) and is_struct(type_.type): |
|
|
|
|
tabulate_data.pop() |
|
|
|
|
for item in info["elems"]: |
|
|
|
|
slot = info["elems"][item].get("slot") |
|
|
|
|
offset = info["elems"][item].get("offset") |
|
|
|
|
size = info["elems"][item].get("size") |
|
|
|
|
type_string = info["elems"][item].get("type_string") |
|
|
|
|
struct_var = info["elems"][item].get("struct_var") |
|
|
|
|
|
|
|
|
|
# doesn't handle deep keys currently |
|
|
|
|
var_name_struct_or_array_var = f"{var} -> {struct_var}" |
|
|
|
|
|
|
|
|
|
tabulate_data.append( |
|
|
|
|
[ |
|
|
|
|
slot, |
|
|
|
|
offset, |
|
|
|
|
size, |
|
|
|
|
type_string, |
|
|
|
|
var_name_struct_or_array_var, |
|
|
|
|
self.convert_value_to_type( |
|
|
|
|
get_storage_data(self.web3, self.checksum_address, slot), |
|
|
|
|
size, |
|
|
|
|
offset, |
|
|
|
|
type_string, |
|
|
|
|
), |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if is_array(type_): |
|
|
|
|
tabulate_data.pop() |
|
|
|
|
for item in info["elems"]: |
|
|
|
|
for key in info["elems"][item]: |
|
|
|
|
slot = info["elems"][item][key].get("slot") |
|
|
|
|
offset = info["elems"][item][key].get("offset") |
|
|
|
|
size = info["elems"][item][key].get("size") |
|
|
|
|
type_string = info["elems"][item][key].get("type_string") |
|
|
|
|
struct_var = info["elems"][item][key].get("struct_var") |
|
|
|
|
|
|
|
|
|
# doesn't handle deep keys currently |
|
|
|
|
var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" |
|
|
|
|
|
|
|
|
|
hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) |
|
|
|
|
tabulate_data.append( |
|
|
|
|
[ |
|
|
|
|
slot, |
|
|
|
|
offset, |
|
|
|
|
size, |
|
|
|
|
type_string, |
|
|
|
|
var_name_struct_or_array_var, |
|
|
|
|
self.convert_value_to_type(hex_bytes, size, offset, type_string), |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
print( |
|
|
|
|
tabulate( |
|
|
|
|
tabulate_data, |
|
|
|
|
headers=["slot", "offset", "size", "type", "name", "value"], |
|
|
|
|
tablefmt="grid", |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def _find_struct_var_slot( |
|
|
|
|
elems: List[StructureVariable], slot: bytes, struct_var: str |
|
|
|
@ -456,9 +621,10 @@ class SlitherReadStorage: |
|
|
|
|
size = byte_size * 8 # bits |
|
|
|
|
(int_slot, offset) = contract.compilation_unit.storage_layout_of(contract, target_variable) |
|
|
|
|
offset *= 8 # bits |
|
|
|
|
logger.info( |
|
|
|
|
f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" |
|
|
|
|
) |
|
|
|
|
if environ.get("SILENT") is None: |
|
|
|
|
logger.info( |
|
|
|
|
f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
return int_slot, size, offset, type_to |
|
|
|
|
|
|
|
|
|