Skip to content

Commit

Permalink
lint
Browse files Browse the repository at this point in the history
lint and fix workflow
  • Loading branch information
0xalpharush committed Feb 11, 2022
1 parent 0b62138 commit 17e942b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 49 deletions.
20 changes: 10 additions & 10 deletions .github/workflows/read_storage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v2

- name: Install ganache
- uses: actions/setup-node@v2
- uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
cache: 'npm'
- run: npm install -g ganache

- name: Install ganache
run: npm install --global ganache

- name: Set up Python 3.6
uses: actions/setup-python@v2
Expand All @@ -36,9 +35,10 @@ jobs:

- name: Install python dependencies
run: |
pip install .
pip install web3
python3 setup.py install
pip install web3 pytest deepdiff solc-select
solc-select install 0.8.1
solc-select use 0.8.1
- name: Run tests
run: |
Expand Down
57 changes: 38 additions & 19 deletions slither/tools/read_storage/read_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@
from eth_typing.evm import ChecksumAddress
from eth_abi import decode_single, encode_abi
from eth_utils import keccak
from .utils import *
from .utils import (
is_array,
is_mapping,
is_struct,
is_user_defined_type,
get_offset_value,
get_storage_data,
coerce_type,
)
except ImportError:
print("ERROR: in order to use slither-read-storage, you need to install web3")
print("$ pip3 install web3 --user\n")
Expand All @@ -31,28 +39,30 @@ class SlitherReadStorageException(Exception):
pass


def _all_struct_variables(var, contracts, address, var_name, rpc_url, storage_address, key=None):
# pylint: disable=too-many-arguments
def _all_struct_variables(var, contracts, address, rpc_url, storage_address=None, key=None):
"""Retrieves all members of a struct."""
if isinstance(var.type.type, StructureContract):
struct_elems = var.type.type.elems_ordered
else:
struct_elems = var.type.type.type.elems_ordered
data = {}
for j in range(len(struct_elems)):
for _, elem in enumerate(struct_elems):
slot, val, type_string = get_storage_slot_and_val(
contracts,
address,
var_name,
var.name,
rpc_url,
storage_address,
key=key,
struct_var=struct_elems[j].name,
struct_var=elem.name,
)
data[struct_elems[j].name] = {"slot": slot, "value": val, "type_string": type_string}
data[elem.name] = {"slot": slot, "value": val, "type_string": type_string}

return data


# pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks
def get_storage_layout(
contracts: List[Contract],
address: str,
Expand Down Expand Up @@ -84,7 +94,7 @@ def get_storage_layout(

if is_user_defined_type(type_) and is_struct(type_.type):
tmp[var_name]["elems"] = _all_struct_variables(
var, contracts, address, var_name, rpc_url, storage_address
var, contracts, address, rpc_url, storage_address
)
continue

Expand All @@ -100,7 +110,7 @@ def get_storage_layout(
if is_user_defined_type(type_.type):
for i in range(min(val, max_depth)):
elems[i] = _all_struct_variables(
var, contracts, address, var_name, rpc_url, storage_address, key=str(i)
var, contracts, address, rpc_url, storage_address, key=str(i)
)
continue

Expand Down Expand Up @@ -148,14 +158,15 @@ def get_storage_layout(
json.dump(data, f, indent=4)


# pylint: disable=too-many-statements,too-many-branches,inconsistent-return-statements
def get_storage_slot_and_val(
contracts: List[Contract],
address: str,
var_name: str,
rpc: str,
storage_address: str = None,
**kwargs,
) -> Tuple[bytes, Any]:
) -> Tuple[int, Any, str]:
"""Finds the storage slot of a variable in a contract by its name and retrieves the slot and data.
Args:
contracts (List[`Contract`]): List of contracts from a slither analyzer object.
Expand All @@ -168,8 +179,9 @@ def get_storage_slot_and_val(
deep_key (str): Key of a mapping embedded within another mapping or secondary index if array.
struct_var (str): Structure variable name.
Returns:
slot (bytes): The storage location of the variable.
slot (int): The storage location of the variable.
value (Any): The type representation of the variable's data.
type_to (str): What type the variable is.
Raises:
SlitherReadStorageException: if the variable is not found.
"""
Expand All @@ -179,7 +191,6 @@ def get_storage_slot_and_val(
address # Default to implementation address unless a storage address is given
)

contract_name = kwargs.get("contract_name", None)
key = kwargs.get("key", None)
deep_key = kwargs.get("deep_key", None)
struct_var = kwargs.get("struct_var", None)
Expand Down Expand Up @@ -235,25 +246,28 @@ def get_storage_slot_and_val(
)
log += info
else:
value = get_storage_data(web3, checksum_address, slot).hex()
int_slot = int.from_bytes(slot, byteorder="big")
return (
int.from_bytes(slot, byteorder="big"),
get_storage_data(web3, checksum_address, slot).hex(),
int_slot,
value,
type_to,
)

elif is_user_defined_type(target_variable.type):
if struct_var:
var_log_name = struct_var
type_to = target_variable.type.type.name
elems = target_variable.type.type.elems_ordered
info, type_to, slot, size, offset = _find_struct_var_slot(
elems, slot, struct_var
)
log += info
else:
value = get_storage_data(web3, checksum_address, slot).hex()
int_slot = int.from_bytes(slot, byteorder="big")
return (
int.from_bytes(slot, byteorder="big"),
get_storage_data(web3, checksum_address, slot).hex(),
int_slot,
value,
type_to,
)

Expand All @@ -264,24 +278,28 @@ def get_storage_slot_and_val(
)
log += info
else:
value = get_storage_data(web3, checksum_address, slot).hex()
int_slot = int.from_bytes(slot, byteorder="big")
return (
int.from_bytes(slot, byteorder="big"),
get_storage_data(web3, checksum_address, slot).hex(),
int_slot,
value,
type_to,
)

else: # elementary type
type_to = target_variable.type.name

hex_bytes = get_storage_data(web3, checksum_address, slot)
int_slot = int.from_bytes(slot, byteorder="big")

# Account for storage packing
offset_hex_bytes = get_offset_value(hex_bytes, offset, size)
value = coerce_type(type_to, offset_hex_bytes)

log += f"\nName: {var_log_name}\nType: {type_to}\nValue: {value}\nSlot: {int.from_bytes(slot, byteorder='big')}\n"
logger.info(log)

return int.from_bytes(slot, byteorder="big"), value, type_to
return int_slot, value, type_to

if not found:
raise SlitherReadStorageException("%s was not found in %s" % (var_name, address))
Expand Down Expand Up @@ -319,6 +337,7 @@ def _find_struct_var_slot(
return info, type_to, slot, size, offset


# pylint: disable=too-many-statements,too-many-branches
def _find_array_slot(
target_variable: StateVariable,
slot: bytes,
Expand Down
40 changes: 20 additions & 20 deletions tests/test_read_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import os
import sys
import json
import pytest
import shutil
import subprocess
from time import sleep
from typing import Generator

import pytest
from deepdiff import DeepDiff
from dataclasses import dataclass
from slither import Slither
from slither.tools.read_storage import get_storage_layout

Expand All @@ -23,11 +23,11 @@
STORAGE_TEST_ROOT = os.path.join(SLITHER_ROOT, "tests", "storage-layout")


@dataclass
class GanacheInstance:
provider: str
eth_address: str
eth_privkey: str
def __init__(self, provider: str, eth_address: str, eth_privkey: str):
self.provider = provider
self.eth_address = eth_address
self.eth_privkey = eth_privkey


@pytest.fixture(scope="module")
Expand All @@ -49,7 +49,7 @@ def ganache() -> Generator[GanacheInstance, None, None]:
eth_privkey = "0xe48ba530a63326818e116be262fd39ae6dcddd89da4b1f578be8afd4e8894b8d"
eth = int(1e18 * 1e6)
port = 8545
p = subprocess.Popen(
with subprocess.Popen(
f"""ganache
--port {port}
--chain.networkId 1
Expand All @@ -59,24 +59,23 @@ def ganache() -> Generator[GanacheInstance, None, None]:
"\n", " "
),
shell=True,
)
) as p:

sleep(3)
yield GanacheInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey)
p.kill()
p.wait()
sleep(3)
yield GanacheInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey)
p.kill()
p.wait()


def get_source_file(file_path):
with open(file_path, "r") as f:
with open(file_path, "r", encoding="utf8") as f:
source = f.read()

return source


def deploy_contract(w3, ganache, contract_bin, contract_abi):
"""Deploy contract to the local ganache network"""
print("balance", w3.eth.get_balance(ganache.eth_address))
signed_txn = w3.eth.account.sign_transaction(
dict(
nonce=w3.eth.get_transaction_count(ganache.eth_address),
Expand All @@ -95,14 +94,15 @@ def deploy_contract(w3, ganache, contract_bin, contract_abi):
return contract


# pylint: disable=too-many-locals
@pytest.mark.usefixtures("web3", "ganache")
def test_read_storage(web3, ganache):
assert web3.isConnected()
bin_path = os.path.join(STORAGE_TEST_ROOT, "StorageLayout.bin")
abi_path = os.path.join(STORAGE_TEST_ROOT, "StorageLayout.abi")
bin = get_source_file(bin_path)
bytecode = get_source_file(bin_path)
abi = get_source_file(abi_path)
contract = deploy_contract(web3, ganache, bin, abi)
contract = deploy_contract(web3, ganache, bytecode, abi)
contract.functions.store().transact({"from": ganache.eth_address})
address = contract.address

Expand All @@ -113,19 +113,19 @@ def test_read_storage(web3, ganache):
expected_file = os.path.join(STORAGE_TEST_ROOT, "TEST_storage_layout.json")
actual_file = os.path.join(SLITHER_ROOT, f"{address}_storage_layout.json")

with open(expected_file, "r") as f:
with open(expected_file, "r", encoding="utf8") as f:
expected = json.load(f)
with open(actual_file, "r") as f:
with open(actual_file, "r", encoding="utf8") as f:
actual = json.load(f)

diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree")
if diff:
for change in diff.get("values_changed", []):
path_list = re.findall(r"\['(.*?)'\]", change.path())
path = "_".join(path_list)
with open(f"{path}_expected.txt", "w") as f:
with open(f"{path}_expected.txt", "w", encoding="utf8") as f:
f.write(change.t1)
with open(f"{path}_actual.txt", "w") as f:
with open(f"{path}_actual.txt", "w", encoding="utf8") as f:
f.write(change.t2)

assert not diff

0 comments on commit 17e942b

Please sign in to comment.