Merge pull request #1385 from crytic/read-storage-from-block

Read storage from block
pull/1373/merge
Feist Josselin 2 years ago committed by GitHub
commit bf73cd1afb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 1
      slither/tools/read_storage/README.md
  3. 11
      slither/tools/read_storage/__main__.py
  4. 17
      slither/tools/read_storage/read_storage.py
  5. 7
      slither/tools/read_storage/utils/utils.py

1
.gitignore vendored

@ -47,6 +47,7 @@ coverage.xml
*.cover *.cover
.hypothesis/ .hypothesis/
.vscode/ .vscode/
storage_layout.json
# Translations # Translations
*.mo *.mo
*.pot *.pot

@ -21,6 +21,7 @@ optional arguments:
--json FILE Write the entire storage layout in JSON format to the specified FILE --json FILE Write the entire storage layout in JSON format to the specified FILE
--value Toggle used to include values in output. --value Toggle used to include values in output.
--max-depth MAX_DEPTH Max depth to search in data structure. --max-depth MAX_DEPTH Max depth to search in data structure.
--block BLOCK_NUMBER Block number to retrieve storage from (requires archive rpc node)
``` ```
### Examples ### Examples

@ -98,6 +98,12 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--max-depth", help="Max depth to search in data structure.", default=20) parser.add_argument("--max-depth", help="Max depth to search in data structure.", default=20)
parser.add_argument(
"--block",
help="The block number to read storage from. Requires an archive node to be provided as the RPC url.",
default="latest",
)
cryticparser.init(parser) cryticparser.init(parser)
return parser.parse_args() return parser.parse_args()
@ -122,6 +128,11 @@ def main() -> None:
srs = SlitherReadStorage(contracts, args.max_depth) srs = SlitherReadStorage(contracts, args.max_depth)
try:
srs.block = int(args.block)
except ValueError:
srs.block = str(args.block or "latest")
if args.rpc_url: if args.rpc_url:
# Remove target prefix e.g. rinkeby:0x0 -> 0x0. # Remove target prefix e.g. rinkeby:0x0 -> 0x0.
address = target[target.find(":") + 1 :] address = target[target.find(":") + 1 :]

@ -53,15 +53,16 @@ class SlitherReadStorageException(Exception):
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
class SlitherReadStorage: class SlitherReadStorage:
def __init__(self, contracts: List[Contract], max_depth: int) -> None: def __init__(self, contracts: List[Contract], max_depth: int) -> None:
self._checksum_address: Optional[ChecksumAddress] = None
self._contracts: List[Contract] = contracts self._contracts: List[Contract] = contracts
self._max_depth: int = max_depth
self._log: str = "" self._log: str = ""
self._max_depth: int = max_depth
self._slot_info: Dict[str, SlotInfo] = {} self._slot_info: Dict[str, SlotInfo] = {}
self._target_variables: List[Tuple[Contract, StateVariable]] = [] self._target_variables: List[Tuple[Contract, StateVariable]] = []
self._web3: Optional[Web3] = None self._web3: Optional[Web3] = None
self._checksum_address: Optional[ChecksumAddress] = None self.block: Union[str, int] = "latest"
self.storage_address: Optional[str] = None
self.rpc: Optional[str] = None self.rpc: Optional[str] = None
self.storage_address: Optional[str] = None
self.table: Optional[MyPrettyTable] = None self.table: Optional[MyPrettyTable] = None
@property @property
@ -231,7 +232,10 @@ class SlitherReadStorage:
:param slot_info: :param slot_info:
""" """
hex_bytes = get_storage_data( hex_bytes = get_storage_data(
self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") self.web3,
self.checksum_address,
int.to_bytes(slot_info.slot, 32, byteorder="big"),
self.block,
) )
slot_info.value = self.convert_value_to_type( slot_info.value = self.convert_value_to_type(
hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string
@ -609,7 +613,10 @@ class SlitherReadStorage:
# Convert from hexadecimal to decimal. # Convert from hexadecimal to decimal.
val = int( val = int(
get_storage_data( get_storage_data(
self.web3, self.checksum_address, int.to_bytes(slot, 32, byteorder="big") self.web3,
self.checksum_address,
int.to_bytes(slot, 32, byteorder="big"),
self.block,
).hex(), ).hex(),
16, 16,
) )

@ -55,16 +55,19 @@ def coerce_type(
return value.hex() return value.hex()
def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> bytes: def get_storage_data(
web3, checksum_address: ChecksumAddress, slot: bytes, block: Union[int, str]
) -> bytes:
""" """
Retrieves the storage data from the blockchain at target address and slot. Retrieves the storage data from the blockchain at target address and slot.
Args: Args:
web3: Web3 instance provider. web3: Web3 instance provider.
checksum_address (ChecksumAddress): The address to query. checksum_address (ChecksumAddress): The address to query.
slot (bytes): The slot to retrieve data from. slot (bytes): The slot to retrieve data from.
block (optional int|str): The block number to retrieve data from
Returns: Returns:
(HexBytes): The slot's storage data. (HexBytes): The slot's storage data.
""" """
return bytes(web3.eth.get_storage_at(checksum_address, slot)).rjust( return bytes(web3.eth.get_storage_at(checksum_address, slot, block)).rjust(
32, bytes(1) 32, bytes(1)
) # pad to 32 bytes ) # pad to 32 bytes

Loading…
Cancel
Save