Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slither-read-storage native POA support #1843

Merged
merged 26 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8cee05d
Native support for POA networks in read_storage
webthethird Apr 17, 2023
c4bb804
Set `srs.rpc` before `srs.block`
webthethird Apr 27, 2023
b490c54
Type hint
webthethird Apr 27, 2023
5bcaf41
New RpcInfo class w/ RpcInfo.web3 and RpcInfo.block
webthethird Apr 27, 2023
e900e79
Black
webthethird Apr 27, 2023
5b90a52
Update test_read_storage.py
webthethird Apr 27, 2023
55817ab
Add import in __init__.py
webthethird Apr 27, 2023
b28e350
Avoid instantiating SRS twice
webthethird Apr 28, 2023
2645d5e
Add comment about `get_block` for POA networks
webthethird Apr 28, 2023
71e13aa
Pylint
webthethird Apr 28, 2023
4fbbfff
Black
webthethird Apr 28, 2023
b6bc234
Allow other valid block string arguments
webthethird Apr 28, 2023
610052d
`args.block` can be in ["latest", "earliest", "pending", "safe", "fin…
webthethird May 4, 2023
034d12d
Use BlockTag enum class for valid `str` arguments
webthethird May 4, 2023
87f94b4
Tweak `RpcInfo.__init__()` signature
webthethird May 4, 2023
ee42026
get rid of `or "latest"`
webthethird May 4, 2023
80b688d
Import BlockTag
webthethird May 4, 2023
24813cc
Use `web3.types.BlockIdentifier`
webthethird May 4, 2023
d462c12
Revert BlockTag enum
webthethird May 11, 2023
c4cec4c
Pylint and black
webthethird May 11, 2023
5e33519
Replace missing newline
webthethird May 11, 2023
a1f6235
Update slither/tools/read_storage/__main__.py
webthethird May 12, 2023
f38eabe
Drop try/except around args.block parsing
webthethird May 12, 2023
a86ee8f
Remove unused import
webthethird May 18, 2023
83e727e
Merge branch 'dev' into dev-read-storage-poa-support
webthethird May 19, 2023
e5c87aa
Merge branch 'dev' into dev-read-storage-poa-support
webthethird May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion slither/tools/read_storage/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .read_storage import SlitherReadStorage
from .read_storage import SlitherReadStorage, RpcInfo
32 changes: 16 additions & 16 deletions slither/tools/read_storage/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from crytic_compile import cryticparser

from slither import Slither
from slither.tools.read_storage.read_storage import SlitherReadStorage
from slither.tools.read_storage.read_storage import SlitherReadStorage, RpcInfo


def parse_args() -> argparse.Namespace:
Expand Down Expand Up @@ -126,22 +126,22 @@ def main() -> None:
else:
contracts = slither.contracts

srs = SlitherReadStorage(contracts, args.max_depth)

try:
srs.block = int(args.block)
except ValueError:
srs.block = str(args.block or "latest")

rpc_info = None
if args.rpc_url:
# Remove target prefix e.g. rinkeby:0x0 -> 0x0.
address = target[target.find(":") + 1 :]
# Default to implementation address unless a storage address is given.
if not args.storage_address:
args.storage_address = address
srs.storage_address = args.storage_address

srs.rpc = args.rpc_url
try:
block = int(args.block)
except ValueError:
valid = ["latest", "earliest", "pending", "safe", "finalized"]
block = next((v for v in valid if v == args.block), "latest")
webthethird marked this conversation as resolved.
Show resolved Hide resolved
rpc_info = RpcInfo(args.rpc_url, block)

srs = SlitherReadStorage(contracts, args.max_depth, rpc_info)
# Remove target prefix e.g. rinkeby:0x0 -> 0x0.
address = target[target.find(":") + 1 :]
# Default to implementation address unless a storage address is given.
if not args.storage_address:
args.storage_address = address
srs.storage_address = args.storage_address

if args.variable_name:
# Use a lambda func to only return variables that have same name as target.
Expand Down
57 changes: 40 additions & 17 deletions slither/tools/read_storage/read_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

from eth_abi import decode, encode
from eth_typing.evm import ChecksumAddress
from eth_utils import keccak
from eth_utils import keccak, to_checksum_address
from web3 import Web3
from web3.types import BlockIdentifier
from web3.exceptions import ExtraDataLengthError
from web3.middleware import geth_poa_middleware

from slither.core.declarations import Contract, Structure
from slither.core.solidity_types import ArrayType, ElementaryType, MappingType, UserDefinedType
Expand Down Expand Up @@ -42,18 +45,43 @@ class SlitherReadStorageException(Exception):
pass


class RpcInfo:
def __init__(self, rpc_url: str, block: BlockIdentifier = "latest") -> None:
assert isinstance(block, int) or block in [
"latest",
"earliest",
"pending",
"safe",
"finalized",
]
self.rpc: str = rpc_url
self._web3: Web3 = Web3(Web3.HTTPProvider(self.rpc))
"""If the RPC is for a POA network, the first call to get_block fails, so we inject geth_poa_middleware"""
try:
webthethird marked this conversation as resolved.
Show resolved Hide resolved
self._block: int = self.web3.eth.get_block(block)["number"]
except ExtraDataLengthError:
self._web3.middleware_onion.inject(geth_poa_middleware, layer=0)
self._block: int = self.web3.eth.get_block(block)["number"]

@property
def web3(self) -> Web3:
return self._web3

@property
def block(self) -> int:
return self._block


# pylint: disable=too-many-instance-attributes
class SlitherReadStorage:
def __init__(self, contracts: List[Contract], max_depth: int) -> None:
def __init__(self, contracts: List[Contract], max_depth: int, rpc_info: RpcInfo = None) -> None:
self._checksum_address: Optional[ChecksumAddress] = None
self._contracts: List[Contract] = contracts
self._log: str = ""
self._max_depth: int = max_depth
self._slot_info: Dict[str, SlotInfo] = {}
self._target_variables: List[Tuple[Contract, StateVariable]] = []
self._web3: Optional[Web3] = None
self.block: Union[str, int] = "latest"
self.rpc: Optional[str] = None
self.rpc_info: Optional[RpcInfo] = rpc_info
self.storage_address: Optional[str] = None
self.table: Optional[MyPrettyTable] = None

Expand All @@ -73,18 +101,12 @@ def log(self) -> str:
def log(self, log: str) -> None:
self._log = log

@property
def web3(self) -> Web3:
if not self._web3:
self._web3 = Web3(Web3.HTTPProvider(self.rpc))
return self._web3

@property
def checksum_address(self) -> ChecksumAddress:
if not self.storage_address:
raise ValueError
if not self._checksum_address:
self._checksum_address = self.web3.to_checksum_address(self.storage_address)
self._checksum_address = to_checksum_address(self.storage_address)
return self._checksum_address

@property
Expand Down Expand Up @@ -223,11 +245,12 @@ def get_slot_values(self, slot_info: SlotInfo) -> None:
"""Fetches the slot value of `SlotInfo` object
:param slot_info:
"""
assert self.rpc_info is not None
hex_bytes = get_storage_data(
self.web3,
self.rpc_info.web3,
self.checksum_address,
int.to_bytes(slot_info.slot, 32, byteorder="big"),
self.block,
self.rpc_info.block,
)
slot_info.value = self.convert_value_to_type(
hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string
Expand Down Expand Up @@ -600,15 +623,15 @@ def _get_array_length(self, type_: Type, slot: int) -> int:
(int): The length of the array.
"""
val = 0
if self.rpc:
if self.rpc_info:
# The length of dynamic arrays is stored at the starting slot.
# Convert from hexadecimal to decimal.
val = int(
get_storage_data(
self.web3,
self.rpc_info.web3,
self.checksum_address,
int.to_bytes(slot, 32, byteorder="big"),
self.block,
self.rpc_info.block,
).hex(),
16,
)
Expand Down
6 changes: 3 additions & 3 deletions tests/tools/read-storage/test_read_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from web3.contract import Contract

from slither import Slither
from slither.tools.read_storage import SlitherReadStorage
from slither.tools.read_storage import SlitherReadStorage, RpcInfo

TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"

Expand Down Expand Up @@ -103,8 +103,8 @@ def test_read_storage(web3, ganache) -> None:
sl = Slither(Path(TEST_DATA_DIR, "storage_layout-0.8.10.sol").as_posix())
contracts = sl.contracts

srs = SlitherReadStorage(contracts, 100)
srs.rpc = ganache.provider
rpc_info: RpcInfo = RpcInfo(ganache.provider)
srs = SlitherReadStorage(contracts, 100, rpc_info)
srs.storage_address = address
srs.get_all_storage_variables()
srs.get_storage_layout()
Expand Down