Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Tests for the effects of EIP-7251 beacon roots on EIP-7928."""
"""Tests for the effects of EIP-7251 consolidation requests on EIP-7928."""

from typing import List

Expand Down
174 changes: 48 additions & 126 deletions tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@


HEADER_TIMESTAMP = 123456789
EXTRA_DATA_AT_LIMIT = b"\x00\x00\x00"
EXTRA_DATA_AT_LIMIT = b"\x00" * 15
# Max size adjustment extra_data can absorb
# reserves 1 byte so delta=-1 tests stay valid
EXTRA_DATA_TOLERANCE = len(EXTRA_DATA_AT_LIMIT) - 1
BLOCK_GAS_LIMIT = 100_000_000


Expand Down Expand Up @@ -130,6 +133,10 @@ def exact_size_transactions(
The calculation uses caching to avoid recalculating the same block rlp for
each fork. Calculate the block and fill with real sender for testing.

Due to RLP encoding boundaries, certain exact block sizes may be
unachievable (±1 byte). The returned extra_data_len compensates for
any gap so the final block hits the exact target.

Args:
sender: The sender account
block_size_limit: The target block RLP size limit
Expand All @@ -141,6 +148,11 @@ def exact_size_transactions(
be included
withdrawals: Optional list of withdrawals to include in the block

Returns:
Tuple of (transactions, extra_data_len) where extra_data_len is
the number of extra_data bytes needed to hit the exact target
block size.

"""
log_contract = None
if emit_logs:
Expand Down Expand Up @@ -169,7 +181,7 @@ def exact_size_transactions(

if not specific_transaction_to_include and not withdrawals:
# use cached version when possible for performance
transactions, gas_used = _exact_size_transactions_cached(
transactions, extra_data_len = _exact_size_transactions_cached(
block_size_limit,
fork,
gas_limit,
Expand All @@ -179,7 +191,7 @@ def exact_size_transactions(
else:
# Direct calculation, no cache, since `Transaction` / `Withdrawal`
# are not hashable
transactions, gas_used = _exact_size_transactions_impl(
transactions, extra_data_len = _exact_size_transactions_impl(
block_size_limit,
fork,
gas_limit,
Expand All @@ -189,7 +201,7 @@ def exact_size_transactions(
withdrawals=withdrawals,
)

return transactions, gas_used
return transactions, extra_data_len


@lru_cache(maxsize=128)
Expand All @@ -203,6 +215,12 @@ def _exact_size_transactions_cached(
"""
Generate transactions that fill a block to exactly the RLP size limit.
Abstracted with hashable arguments for caching block calculations.

Returns:
Tuple of (transactions, extra_data_len) where extra_data_len is
the number of extra_data bytes needed to hit the exact target
block size.

"""
return _exact_size_transactions_impl(
block_size_limit,
Expand Down Expand Up @@ -339,62 +357,7 @@ def _exact_size_transactions_impl(
gas_limit=target_gas,
data=target_calldata,
)

test_size = get_block_rlp_size(
fork,
transactions + [test_tx],
withdrawals=withdrawals,
)

if test_size == block_size_limit:
# if exact match, use the transaction
transactions.append(test_tx)
else:
# search for the best adjustment
diff = block_size_limit - test_size
best_diff = abs(diff)

search_range = min(abs(diff) + 50, 1000)

for adjustment in range(-search_range, search_range + 1):
adjusted_size = estimated_calldata + adjustment
if adjusted_size < 0:
continue

adjusted_calldata = b"\x00" * adjusted_size
adjusted_gas = calculator(calldata=adjusted_calldata)

if adjusted_gas <= remaining_gas:
adjusted_tx = Transaction(
sender=sender,
nonce=nonce,
max_fee_per_gas=10**11,
max_priority_fee_per_gas=10**11,
gas_limit=adjusted_gas,
data=adjusted_calldata,
)

adjusted_test_size = get_block_rlp_size(
fork,
transactions + [adjusted_tx],
withdrawals=withdrawals,
)

if adjusted_test_size == block_size_limit:
# exact match
transactions.append(adjusted_tx)
break

adjusted_diff = abs(
block_size_limit - adjusted_test_size
)
if adjusted_diff < best_diff:
best_diff = adjusted_diff
else:
raise RuntimeError(
"Failed to find a transaction that matches "
"the target size."
)
transactions.append(test_tx)
else:
transactions.append(empty_tx)

Expand All @@ -403,14 +366,15 @@ def _exact_size_transactions_impl(
transactions,
withdrawals=withdrawals,
)
final_gas = sum(tx.gas_limit for tx in transactions)

assert final_size == block_size_limit, (
# Compute the extra_data length that compensates for any size gap.
size_diff = final_size - block_size_limit
assert abs(size_diff) <= EXTRA_DATA_TOLERANCE, (
f"Size mismatch: got {final_size}, "
f"expected {block_size_limit} "
f"({final_size - block_size_limit} bytes diff)"
f"({size_diff} bytes diff, exceeds ±{EXTRA_DATA_TOLERANCE} tolerance)"
)
return transactions, final_gas
extra_data_len = len(EXTRA_DATA_AT_LIMIT) - size_diff
return transactions, extra_data_len


@EIPChecklist.BlockLevelConstraint.Test.Boundary.Under()
Expand Down Expand Up @@ -446,19 +410,13 @@ def test_block_at_rlp_size_limit_boundary(
- At the limit, the block is valid
- At the limit + 1 byte, the block is invalid
"""
transactions, gas_used = exact_size_transactions(
transactions, extra_data_len = exact_size_transactions(
sender,
block_size_limit,
fork,
pre,
env.gas_limit,
)
block_rlp_size = get_block_rlp_size(fork, transactions)
assert block_rlp_size == block_size_limit, (
f"Block RLP size {block_rlp_size} does not exactly match "
f"limit {block_size_limit}, difference: "
f"{block_rlp_size - block_size_limit} bytes"
)

block = Block(
txs=transactions,
Expand All @@ -467,12 +425,8 @@ def test_block_at_rlp_size_limit_boundary(
else None,
)

if delta < 0:
block.extra_data = Bytes(EXTRA_DATA_AT_LIMIT[: -abs(delta)])
elif delta == 0:
block.extra_data = Bytes(EXTRA_DATA_AT_LIMIT)
else: # delta > 0
block.extra_data = Bytes(EXTRA_DATA_AT_LIMIT + b"\x00" * delta)
target_extra_data_len = max(extra_data_len + delta, 0)
block.extra_data = Bytes(b"\x00" * target_extra_data_len)

block.timestamp = ZeroPaddedHexNumber(HEADER_TIMESTAMP)
blockchain_test(
Expand All @@ -498,23 +452,17 @@ def test_block_rlp_size_at_limit_with_all_typed_transactions(
typed_transaction: Transaction,
) -> None:
"""Test the block RLP size limit with all transaction types."""
transactions, gas_used = exact_size_transactions(
transactions, extra_data_len = exact_size_transactions(
sender,
block_size_limit,
fork,
pre,
env.gas_limit,
specific_transaction_to_include=typed_transaction,
)
block_rlp_size = get_block_rlp_size(fork, transactions)
assert block_rlp_size == block_size_limit, (
f"Block RLP size {block_rlp_size} does not exactly match limit "
f"{block_size_limit}, difference: {block_rlp_size - block_size_limit} "
"bytes"
)

block = Block(txs=transactions)
block.extra_data = Bytes(EXTRA_DATA_AT_LIMIT)
block.extra_data = Bytes(b"\x00" * extra_data_len)
block.timestamp = ZeroPaddedHexNumber(HEADER_TIMESTAMP)

blockchain_test(
Expand All @@ -541,7 +489,7 @@ def test_block_at_rlp_limit_with_logs(
Test that a block at the RLP size limit is valid even when transactions
emit logs.
"""
transactions, gas_used = exact_size_transactions(
transactions, extra_data_len = exact_size_transactions(
sender,
block_size_limit,
fork,
Expand All @@ -550,15 +498,8 @@ def test_block_at_rlp_limit_with_logs(
emit_logs=True,
)

block_rlp_size = get_block_rlp_size(fork, transactions)
assert block_rlp_size == block_size_limit, (
f"Block RLP size {block_rlp_size} does not exactly match limit "
f"{block_size_limit}, difference: {block_rlp_size - block_size_limit} "
"bytes"
)

block = Block(txs=transactions)
block.extra_data = Bytes(EXTRA_DATA_AT_LIMIT)
block.extra_data = Bytes(b"\x00" * extra_data_len)
block.timestamp = ZeroPaddedHexNumber(HEADER_TIMESTAMP)

blockchain_test(
Expand Down Expand Up @@ -600,7 +541,7 @@ def test_block_at_rlp_limit_with_withdrawals(
),
]

transactions, gas_used = exact_size_transactions(
transactions, extra_data_len = exact_size_transactions(
sender,
block_size_limit,
fork,
Expand All @@ -609,19 +550,10 @@ def test_block_at_rlp_limit_with_withdrawals(
withdrawals=withdrawals,
)

block_rlp_size = get_block_rlp_size(
fork, transactions, withdrawals=withdrawals
)
assert block_rlp_size == block_size_limit, (
f"Block RLP size {block_rlp_size} does not exactly match limit "
f"{block_size_limit}, difference: {block_rlp_size - block_size_limit} "
"bytes"
)

block = Block(
txs=transactions,
withdrawals=withdrawals,
extra_data=Bytes(EXTRA_DATA_AT_LIMIT),
extra_data=Bytes(b"\x00" * extra_data_len),
timestamp=ZeroPaddedHexNumber(HEADER_TIMESTAMP),
)

Expand Down Expand Up @@ -664,47 +596,37 @@ def test_fork_transition_block_rlp_limit(
sender_before_fork = pre.fund_eoa()
sender_at_fork = pre.fund_eoa()

transactions_before, gas_used_before = exact_size_transactions(
transactions_before, extra_data_len_before = exact_size_transactions(
sender_before_fork,
block_size_limit,
fork,
pre,
env.gas_limit,
)

transactions_at_fork, gas_used_at_fork = exact_size_transactions(
transactions_at_fork, extra_data_len_at_fork = exact_size_transactions(
sender_at_fork,
block_size_limit,
fork,
pre,
env.gas_limit,
)

for fork_block_rlp_size in [
get_block_rlp_size(fork, transactions_before),
get_block_rlp_size(fork, transactions_at_fork),
]:
assert fork_block_rlp_size == block_size_limit, (
f"Block RLP size {fork_block_rlp_size} does not exactly match "
f"limit {block_size_limit}, difference: "
f"{fork_block_rlp_size - block_size_limit} bytes"
)

# HEADER_TIMESTAMP (123456789) used in calculation takes 4 bytes in RLP
# encoding. Transition timestamps (14_999 and 15_000) take 2 bytes
# Re-define `_extradata_at_limit` accounting for this difference
# encoding. Transition timestamps (14_999 and 15_000) take 2 bytes.
# Add the difference to extra_data to keep block at the limit.
timestamp_byte_savings = 2
_extradata_at_limit = EXTRA_DATA_AT_LIMIT + (
b"\x00" * timestamp_byte_savings
)

extra_data_before = extra_data_len_before + timestamp_byte_savings
extra_data_at_fork = extra_data_len_at_fork + timestamp_byte_savings

blocks = [
# before fork, block at limit +1 should be accepted
Block(
timestamp=14_999,
txs=transactions_before,
# +1 to exceed limit
extra_data=Bytes(_extradata_at_limit + b"\x00"),
extra_data=Bytes(b"\x00" * (extra_data_before + 1)),
)
]

Expand All @@ -715,7 +637,7 @@ def test_fork_transition_block_rlp_limit(
timestamp=15_000,
txs=transactions_at_fork,
# +1 to exceed limit, should be rejected
extra_data=Bytes(_extradata_at_limit + b"\x00"),
extra_data=Bytes(b"\x00" * (extra_data_at_fork + 1)),
exception=BlockException.RLP_BLOCK_LIMIT_EXCEEDED,
)
)
Expand All @@ -725,7 +647,7 @@ def test_fork_transition_block_rlp_limit(
timestamp=15_000,
txs=transactions_at_fork,
# exact limit should be accepted
extra_data=Bytes(EXTRA_DATA_AT_LIMIT),
extra_data=Bytes(b"\x00" * extra_data_at_fork),
)
)

Expand Down
Loading