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

Add Black checks to beacon module, scripts/, and gas_strategies #2520

Merged
merged 4 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
69f49ee1ffa8ca98d66d9b960976a490c846c82f
831ff0f64a6db983cc2695af803942091ea877a1
784ece4e96249027607e66b4ebceb3cfde268ce8
fa4f9708924384e3af597915ccf7b39968d91062
e961762b6e1a3840799e26fd9185a353730a5b79
1429bdd60081661d343262eee3c782d89017937c
1 change: 1 addition & 0 deletions newsfragments/2520.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Black lint checks to Beacon module, ``web3/gas_strategies``, and ``web3/scripts``
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ basepython=python
extras=linter
commands=
flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/ethpm {toxinidir}/tests --exclude {toxinidir}/ethpm/ethpm-spec
black {toxinidir}/ethpm {toxinidir}/web3/auto {toxinidir}/web3/utils {toxinidir}/web3/_utils --exclude {toxinidir}/ethpm/ethpm-spec --check
black {toxinidir}/ethpm {toxinidir}/web3/auto {toxinidir}/web3/utils {toxinidir}/web3/_utils {toxinidir}/web3/beacon {toxinidir}/web3/gas_strategies {toxinidir}/web3/scripts --exclude {toxinidir}/ethpm/ethpm-spec --check
isort --recursive --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/ethpm/ {toxinidir}/tests/
mypy -p web3 -p ethpm -p ens --config-file {toxinidir}/mypy.ini

Expand Down
5 changes: 3 additions & 2 deletions web3/gas_strategies/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
)


def rpc_gas_price_strategy(w3: Web3,
transaction_params: Optional[TxParams] = None) -> Wei:
def rpc_gas_price_strategy(
w3: Web3, transaction_params: Optional[TxParams] = None
) -> Wei:
"""
A simple gas price strategy deriving it's value from the eth_gasPrice JSON-RPC call.
"""
Expand Down
81 changes: 47 additions & 34 deletions web3/gas_strategies/time_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,38 +38,40 @@
)

MinerData = collections.namedtuple(
'MinerData',
['miner', 'num_blocks', 'min_gas_price', 'low_percentile_gas_price'])
Probability = collections.namedtuple('Probability', ['gas_price', 'prob'])
"MinerData", ["miner", "num_blocks", "min_gas_price", "low_percentile_gas_price"]
)
Probability = collections.namedtuple("Probability", ["gas_price", "prob"])


def _get_avg_block_time(w3: Web3, sample_size: int) -> float:
latest = w3.eth.get_block('latest')
latest = w3.eth.get_block("latest")

constrained_sample_size = min(sample_size, latest['number'])
constrained_sample_size = min(sample_size, latest["number"])
if constrained_sample_size == 0:
raise ValidationError('Constrained sample size is 0')
raise ValidationError("Constrained sample size is 0")

oldest = w3.eth.get_block(BlockNumber(latest['number'] - constrained_sample_size))
return (latest['timestamp'] - oldest['timestamp']) / constrained_sample_size
oldest = w3.eth.get_block(BlockNumber(latest["number"] - constrained_sample_size))
return (latest["timestamp"] - oldest["timestamp"]) / constrained_sample_size


def _get_weighted_avg_block_time(w3: Web3, sample_size: int) -> float:
latest_block_number = w3.eth.get_block('latest')['number']
latest_block_number = w3.eth.get_block("latest")["number"]
constrained_sample_size = min(sample_size, latest_block_number)
if constrained_sample_size == 0:
raise ValidationError('Constrained sample size is 0')
raise ValidationError("Constrained sample size is 0")

oldest_block = w3.eth.get_block(BlockNumber(latest_block_number - constrained_sample_size))
oldest_block_number = oldest_block['number']
prev_timestamp = oldest_block['timestamp']
oldest_block = w3.eth.get_block(
BlockNumber(latest_block_number - constrained_sample_size)
)
oldest_block_number = oldest_block["number"]
prev_timestamp = oldest_block["timestamp"]
weighted_sum = 0.0
sum_of_weights = 0.0
for i in range(oldest_block_number + 1, latest_block_number + 1):
curr_timestamp = w3.eth.get_block(BlockNumber(i))['timestamp']
curr_timestamp = w3.eth.get_block(BlockNumber(i))["timestamp"]
time = curr_timestamp - prev_timestamp
weight = (i - oldest_block_number) / constrained_sample_size
weighted_sum += (time * weight)
weighted_sum += time * weight
sum_of_weights += weight
prev_timestamp = curr_timestamp
return weighted_sum / sum_of_weights
Expand All @@ -78,24 +80,24 @@ def _get_weighted_avg_block_time(w3: Web3, sample_size: int) -> float:
def _get_raw_miner_data(
w3: Web3, sample_size: int
) -> Iterable[Tuple[ChecksumAddress, HexBytes, Wei]]:
latest = w3.eth.get_block('latest', full_transactions=True)
latest = w3.eth.get_block("latest", full_transactions=True)

for transaction in latest['transactions']:
for transaction in latest["transactions"]:
# type ignored b/c actual transaction is TxData not HexBytes
yield (latest['miner'], latest['hash'], transaction['gasPrice']) # type: ignore
yield (latest["miner"], latest["hash"], transaction["gasPrice"]) # type: ignore

block = latest

for _ in range(sample_size - 1):
if block['number'] == 0:
if block["number"] == 0:
break

# we intentionally trace backwards using parent hashes rather than
# block numbers to make caching the data easier to implement.
block = w3.eth.get_block(block['parentHash'], full_transactions=True)
for transaction in block['transactions']:
block = w3.eth.get_block(block["parentHash"], full_transactions=True)
for transaction in block["transactions"]:
# type ignored b/c actual transaction is TxData not HexBytes
yield (block['miner'], block['hash'], transaction['gasPrice']) # type: ignore
yield (block["miner"], block["hash"], transaction["gasPrice"]) # type: ignore


def _aggregate_miner_data(
Expand All @@ -114,7 +116,8 @@ def _aggregate_miner_data(
miner,
len(set(block_hashes)),
min(gas_prices), # type: ignore
price_percentile)
price_percentile,
)


@to_tuple
Expand All @@ -125,20 +128,26 @@ def _compute_probabilities(
Computes the probabilities that a txn will be accepted at each of the gas
prices accepted by the miners.
"""
miner_data_by_price = tuple(sorted(
miner_data,
key=operator.attrgetter('low_percentile_gas_price'),
reverse=True,
))
miner_data_by_price = tuple(
sorted(
miner_data,
key=operator.attrgetter("low_percentile_gas_price"),
reverse=True,
)
)
for idx in range(len(miner_data_by_price)):
low_percentile_gas_price = miner_data_by_price[idx].low_percentile_gas_price
num_blocks_accepting_price = sum(m.num_blocks for m in miner_data_by_price[idx:])
num_blocks_accepting_price = sum(
m.num_blocks for m in miner_data_by_price[idx:]
)
inv_prob_per_block = (sample_size - num_blocks_accepting_price) / sample_size
probability_accepted = 1 - inv_prob_per_block ** wait_blocks
probability_accepted = 1 - inv_prob_per_block**wait_blocks
yield Probability(low_percentile_gas_price, probability_accepted)


def _compute_gas_price(probabilities: Sequence[Probability], desired_probability: float) -> Wei:
def _compute_gas_price(
probabilities: Sequence[Probability], desired_probability: float
) -> Wei:
"""
Given a sorted range of ``Probability`` named-tuples returns a gas price
computed based on where the ``desired_probability`` would fall within the
Expand All @@ -163,7 +172,7 @@ def _compute_gas_price(probabilities: Sequence[Probability], desired_probability
# This code block should never be reachable as it would indicate
# that we already passed by the probability window in which our
# `desired_probability` is located.
raise Exception('Invariant')
raise Exception("Invariant")

adj_prob = desired_probability - right.prob
window_size = left.prob - right.prob
Expand All @@ -180,12 +189,15 @@ def _compute_gas_price(probabilities: Sequence[Probability], desired_probability
# reachable would be if the `probabilities` were not sorted correctly.
# Otherwise, the `desired_probability` **must** fall between two of the
# values in the `probabilities``.
raise Exception('Invariant')
raise Exception("Invariant")


@curry
def construct_time_based_gas_price_strategy(
max_wait_seconds: int, sample_size: int = 120, probability: int = 98, weighted: bool = False
max_wait_seconds: int,
sample_size: int = 120,
probability: int = 98,
weighted: bool = False,
) -> GasPriceStrategy:
"""
A gas pricing strategy that uses recently mined block data to derive a gas
Expand Down Expand Up @@ -219,6 +231,7 @@ def time_based_gas_price_strategy(w3: Web3, transaction_params: TxParams) -> Wei

gas_price = _compute_gas_price(probabilities, probability / 100)
return gas_price

return time_based_gas_price_strategy


Expand Down
18 changes: 8 additions & 10 deletions web3/scripts/release/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@


def create_venv(parent_path: Path) -> Path:
venv_path = parent_path / 'package-smoke-test'
venv_path = parent_path / "package-smoke-test"
venv.create(venv_path, with_pip=True)
subprocess.run([venv_path / 'bin' / 'pip', 'install', '-U', 'pip', 'setuptools'], check=True)
subprocess.run(
[venv_path / "bin" / "pip", "install", "-U", "pip", "setuptools"], check=True
)
return venv_path


def find_wheel(project_path: Path) -> Path:
wheels = list(project_path.glob('dist/*.whl'))
wheels = list(project_path.glob("dist/*.whl"))

if len(wheels) != 1:
raise Exception(
Expand All @@ -38,24 +40,20 @@ def install_wheel(
extra_suffix = ""

subprocess.run(
[
venv_path / 'bin' / 'pip',
'install',
f"{wheel_path}{extra_suffix}"
],
[venv_path / "bin" / "pip", "install", f"{wheel_path}{extra_suffix}"],
check=True,
)


def test_install_local_wheel() -> None:
with TemporaryDirectory() as tmpdir:
venv_path = create_venv(Path(tmpdir))
wheel_path = find_wheel(Path('.'))
wheel_path = find_wheel(Path("."))
install_wheel(venv_path, wheel_path)
print("Installed", wheel_path.absolute(), "to", venv_path)
print(f"Activate with `source {venv_path}/bin/activate`")
input("Press enter when the test has completed. The directory will be deleted.")


if __name__ == '__main__':
if __name__ == "__main__":
test_install_local_wheel()