Merge branch 'read-storage-table' of github.com:noxx3xxon/slither into noxx3xxon-read-storage-table

pull/1311/head
Josselin Feist 2 years ago
commit e7a5e7b90d
  1. 1
      .github/workflows/read_storage.yml
  2. 3
      .gitignore
  3. 35
      slither/tools/read_storage/__main__.py
  4. 168
      slither/tools/read_storage/read_storage.py

@ -40,6 +40,7 @@ jobs:
pip install pytest==7.0.1
pip install typing_extensions==4.1.1
pip install importlib_metadata==4.8.3
pip install tabulate==0.8.10
solc-select install 0.8.1
solc-select install 0.8.10
solc-select use 0.8.1

3
.gitignore vendored

@ -107,3 +107,6 @@ ENV/
# Test results
test_artifacts/
# crytic export
crytic-export/

@ -3,6 +3,7 @@ Tool to read on-chain storage from EVM
"""
import json
import argparse
from os import environ
from crytic_compile import cryticparser
@ -84,6 +85,24 @@ def parse_args() -> argparse.Namespace:
help="Toggle used to include values in output.",
)
parser.add_argument(
"--table-storage-layout",
action="store_true",
help="Print table view of storage layout",
)
parser.add_argument(
"--table-storage-value",
action="store_true",
help="Print table view of storage layout & values",
)
parser.add_argument(
"--silent",
action="store_true",
help="Silence log outputs",
)
parser.add_argument("--max-depth", help="Max depth to search in data structure.", default=20)
cryticparser.init(parser)
@ -120,6 +139,22 @@ def main() -> None:
srs.rpc = args.rpc_url
environ["SILENT"] = args.silent
if args.table_storage_layout:
environ["TABLE"] = "1"
srs.get_all_storage_variables()
srs.get_storage_layout()
srs.print_table()
return
if args.table_storage_value:
environ["TABLE"] = "1"
srs.get_all_storage_variables()
srs.get_storage_layout()
srs.print_table_with_values()
return
if args.layout:
srs.get_all_storage_variables()
srs.get_storage_layout()

@ -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"
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,6 +621,7 @@ class SlitherReadStorage:
size = byte_size * 8 # bits
(int_slot, offset) = contract.compilation_unit.storage_layout_of(contract, target_variable)
offset *= 8 # bits
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"
)

Loading…
Cancel
Save